kevin
commited on
Commit
·
0ab3d49
1
Parent(s):
ebbe4aa
Rust
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .env.example +100 -0
- .gitignore +28 -0
- Cargo.lock +2798 -0
- Cargo.toml +55 -0
- Cross.toml +5 -0
- Cursor API.md +179 -0
- Dockerfile +36 -0
- Dockerfile.cross +31 -0
- Dockerfile.cross.arm64 +31 -0
- README.md +1 -0
- scripts/build.ps1 +126 -0
- scripts/build.sh +194 -0
- scripts/minify.js +157 -0
- scripts/package-lock.json +321 -0
- scripts/package.json +14 -0
- scripts/setup.ps1 +179 -0
- scripts/setup.sh +157 -0
- serve.ts +1 -0
- src/app.rs +4 -0
- src/app/config.rs +159 -0
- src/app/constant.rs +83 -0
- src/app/lazy.rs +189 -0
- src/app/model.rs +486 -0
- src/app/model/build_key.rs +74 -0
- src/app/model/config.rs +114 -0
- src/app/model/proxies.rs +81 -0
- src/app/model/usage_check.rs +172 -0
- src/chat.rs +10 -0
- src/chat/adapter.rs +473 -0
- src/chat/aiserver.rs +1 -0
- src/chat/aiserver/v1.rs +57 -0
- src/chat/aiserver/v1/lite.proto +1156 -0
- src/chat/config.rs +30 -0
- src/chat/config/key.proto +46 -0
- src/chat/constant.rs +204 -0
- src/chat/constant/models.rs +118 -0
- src/chat/error.rs +139 -0
- src/chat/middleware.rs +2 -0
- src/chat/middleware/auth.rs +23 -0
- src/chat/model.rs +107 -0
- src/chat/route.rs +19 -0
- src/chat/route/api.rs +26 -0
- src/chat/route/config.rs +204 -0
- src/chat/route/health.rs +140 -0
- src/chat/route/logs.rs +109 -0
- src/chat/route/profile.rs +34 -0
- src/chat/route/tokens.rs +481 -0
- src/chat/service.rs +766 -0
- src/chat/stream.rs +2 -0
- src/chat/stream/decoder.rs +406 -0
.env.example
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 当前配置为默认值,请根据需要修改
|
| 2 |
+
|
| 3 |
+
# 服务器监听端口
|
| 4 |
+
PORT=3000
|
| 5 |
+
|
| 6 |
+
# 路由前缀,必须以 / 开头(如果不为空)
|
| 7 |
+
ROUTE_PREFIX=
|
| 8 |
+
|
| 9 |
+
# 最高权限的认证令牌,必填
|
| 10 |
+
AUTH_TOKEN=
|
| 11 |
+
|
| 12 |
+
# 共享的认证令牌,仅Chat端点权限(轮询与AUTH_TOKEN同步),无其余权限
|
| 13 |
+
SHARED_TOKEN=
|
| 14 |
+
|
| 15 |
+
# 启用流式响应检查,关闭则无法响应错误,代价是会对第一个块解析2次(已弃用)
|
| 16 |
+
# 新版本已经完成优化
|
| 17 |
+
# ENABLE_STREAM_CHECK=true
|
| 18 |
+
|
| 19 |
+
# 流式消息结束后发送包含"finish_reason"为"stop"的空消息块(已弃用)
|
| 20 |
+
# INCLUDE_STOP_REASON_STREAM=true
|
| 21 |
+
|
| 22 |
+
# 令牌文件路径(已弃用)
|
| 23 |
+
# TOKEN_FILE=.token
|
| 24 |
+
|
| 25 |
+
# 令牌列表文件路径
|
| 26 |
+
TOKEN_LIST_FILE=.tokens
|
| 27 |
+
|
| 28 |
+
# (实验性)是否启用慢速池(true/false)
|
| 29 |
+
ENABLE_SLOW_POOL=false
|
| 30 |
+
|
| 31 |
+
# 允许claude开头的模型请求绕过内置模型限制(true/false)
|
| 32 |
+
PASS_ANY_CLAUDE=false
|
| 33 |
+
|
| 34 |
+
# 图片处理能力配置
|
| 35 |
+
# 可选值:
|
| 36 |
+
# - none 或 disabled:禁用图片功能
|
| 37 |
+
# - base64 或 base64-only:仅支持 base64 编码的图片
|
| 38 |
+
# - all 或 base64-http:支持 base64 和 HTTP 图片
|
| 39 |
+
# 注意:启用 HTTP 支持可能会暴露服务器 IP
|
| 40 |
+
VISION_ABILITY=base64
|
| 41 |
+
|
| 42 |
+
# 额度检查配置
|
| 43 |
+
# 可选值:
|
| 44 |
+
# - none 或 disabled:禁用额度检查
|
| 45 |
+
# - default:详见 README
|
| 46 |
+
# - all 或 everything:额度无条件检查
|
| 47 |
+
# - 以,分隔的模型列表,为空时使用默认值
|
| 48 |
+
USAGE_CHECK=default
|
| 49 |
+
|
| 50 |
+
# 是否允许使用动态(自定义)配置的 API Key
|
| 51 |
+
DYNAMIC_KEY=false
|
| 52 |
+
|
| 53 |
+
# 动态 Key 的标识前缀
|
| 54 |
+
KEY_PREFIX=sk-
|
| 55 |
+
|
| 56 |
+
# 默认提示词
|
| 57 |
+
DEFAULT_INSTRUCTIONS="Respond in Chinese by default"
|
| 58 |
+
|
| 59 |
+
# 反向代理服务器主机名
|
| 60 |
+
REVERSE_PROXY_HOST=
|
| 61 |
+
|
| 62 |
+
# 代理地址配置说明
|
| 63 |
+
# - 留空或 `no`: 不使用任何代理
|
| 64 |
+
# - `system`: 使用系统代理(变量不存在时的默认值)
|
| 65 |
+
# - 代理地址: 支持以下格式
|
| 66 |
+
# - 多个代理: `http://localhost:7890,https://username:password@localhost:1234`
|
| 67 |
+
# 没有轮询,只是选择第一个格式正确的
|
| 68 |
+
# - 支持的协议: http, https, socks4, socks5, socks5h
|
| 69 |
+
PROXIES=
|
| 70 |
+
|
| 71 |
+
# 请求体大小限制(单位为MB)
|
| 72 |
+
# 默认为2MB (2,097,152 字节)
|
| 73 |
+
REQUEST_BODY_LIMIT_MB=2
|
| 74 |
+
|
| 75 |
+
# OpenAI 请求时,token 和 checksum 的分隔符
|
| 76 |
+
TOKEN_DELIMITER=,
|
| 77 |
+
|
| 78 |
+
# 同时兼容默认的,作为分隔符
|
| 79 |
+
USE_COMMA_DELIMITER=true
|
| 80 |
+
|
| 81 |
+
# 调试
|
| 82 |
+
DEBUG=false
|
| 83 |
+
|
| 84 |
+
# 调试文件
|
| 85 |
+
DEBUG_LOG_FILE=debug.log
|
| 86 |
+
|
| 87 |
+
# 日志储存条数(最大值2000)
|
| 88 |
+
REQUEST_LOGS_LIMIT=100
|
| 89 |
+
|
| 90 |
+
# Cursor 服务超时(秒)(最大值600)
|
| 91 |
+
SERVICE_TIMEOUT=30
|
| 92 |
+
|
| 93 |
+
# 包含网络引用
|
| 94 |
+
INCLUDE_WEB_REFERENCES=false
|
| 95 |
+
|
| 96 |
+
# 持久化日志文件路径
|
| 97 |
+
LOGS_FILE_PATH=logs.bin
|
| 98 |
+
|
| 99 |
+
# 持久化页面配置文件路径
|
| 100 |
+
PAGES_FILE_PATH=pages.bin
|
.gitignore
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/target
|
| 2 |
+
/tools/*/target
|
| 3 |
+
/*.log
|
| 4 |
+
/*.env
|
| 5 |
+
/static/*.min.html
|
| 6 |
+
/static/*.min.css
|
| 7 |
+
/static/*.min.js
|
| 8 |
+
/scripts/.asset-hashes.json
|
| 9 |
+
node_modules
|
| 10 |
+
.DS_Store
|
| 11 |
+
/.vscode
|
| 12 |
+
/.cargo
|
| 13 |
+
/.token
|
| 14 |
+
/.token-list
|
| 15 |
+
/.tokens
|
| 16 |
+
/cursor-api
|
| 17 |
+
/cursor-api.exe
|
| 18 |
+
/release
|
| 19 |
+
|
| 20 |
+
/*.py
|
| 21 |
+
/logs
|
| 22 |
+
/dev*
|
| 23 |
+
/build*
|
| 24 |
+
/*.bin
|
| 25 |
+
/result.txt
|
| 26 |
+
tools/tokenizer/
|
| 27 |
+
|
| 28 |
+
.idea
|
Cargo.lock
ADDED
|
@@ -0,0 +1,2798 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is automatically @generated by Cargo.
|
| 2 |
+
# It is not intended for manual editing.
|
| 3 |
+
version = 4
|
| 4 |
+
|
| 5 |
+
[[package]]
|
| 6 |
+
name = "addr2line"
|
| 7 |
+
version = "0.24.2"
|
| 8 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 9 |
+
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
| 10 |
+
dependencies = [
|
| 11 |
+
"gimli",
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
[[package]]
|
| 15 |
+
name = "adler2"
|
| 16 |
+
version = "2.0.0"
|
| 17 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 18 |
+
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
| 19 |
+
|
| 20 |
+
[[package]]
|
| 21 |
+
name = "ahash"
|
| 22 |
+
version = "0.7.8"
|
| 23 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 24 |
+
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
| 25 |
+
dependencies = [
|
| 26 |
+
"getrandom",
|
| 27 |
+
"once_cell",
|
| 28 |
+
"version_check",
|
| 29 |
+
]
|
| 30 |
+
|
| 31 |
+
[[package]]
|
| 32 |
+
name = "aho-corasick"
|
| 33 |
+
version = "1.1.3"
|
| 34 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 35 |
+
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
| 36 |
+
dependencies = [
|
| 37 |
+
"memchr",
|
| 38 |
+
]
|
| 39 |
+
|
| 40 |
+
[[package]]
|
| 41 |
+
name = "alloc-no-stdlib"
|
| 42 |
+
version = "2.0.4"
|
| 43 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 44 |
+
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
| 45 |
+
|
| 46 |
+
[[package]]
|
| 47 |
+
name = "alloc-stdlib"
|
| 48 |
+
version = "0.2.2"
|
| 49 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 50 |
+
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
| 51 |
+
dependencies = [
|
| 52 |
+
"alloc-no-stdlib",
|
| 53 |
+
]
|
| 54 |
+
|
| 55 |
+
[[package]]
|
| 56 |
+
name = "android-tzdata"
|
| 57 |
+
version = "0.1.1"
|
| 58 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 59 |
+
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
| 60 |
+
|
| 61 |
+
[[package]]
|
| 62 |
+
name = "android_system_properties"
|
| 63 |
+
version = "0.1.5"
|
| 64 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 65 |
+
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
| 66 |
+
dependencies = [
|
| 67 |
+
"libc",
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
[[package]]
|
| 71 |
+
name = "anyhow"
|
| 72 |
+
version = "1.0.95"
|
| 73 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 74 |
+
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
| 75 |
+
|
| 76 |
+
[[package]]
|
| 77 |
+
name = "async-compression"
|
| 78 |
+
version = "0.4.18"
|
| 79 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 80 |
+
checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522"
|
| 81 |
+
dependencies = [
|
| 82 |
+
"brotli",
|
| 83 |
+
"flate2",
|
| 84 |
+
"futures-core",
|
| 85 |
+
"memchr",
|
| 86 |
+
"pin-project-lite",
|
| 87 |
+
"tokio",
|
| 88 |
+
]
|
| 89 |
+
|
| 90 |
+
[[package]]
|
| 91 |
+
name = "atomic-waker"
|
| 92 |
+
version = "1.1.2"
|
| 93 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 94 |
+
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
| 95 |
+
|
| 96 |
+
[[package]]
|
| 97 |
+
name = "autocfg"
|
| 98 |
+
version = "1.4.0"
|
| 99 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 100 |
+
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
| 101 |
+
|
| 102 |
+
[[package]]
|
| 103 |
+
name = "axum"
|
| 104 |
+
version = "0.8.1"
|
| 105 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 106 |
+
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
|
| 107 |
+
dependencies = [
|
| 108 |
+
"axum-core",
|
| 109 |
+
"bytes",
|
| 110 |
+
"form_urlencoded",
|
| 111 |
+
"futures-util",
|
| 112 |
+
"http",
|
| 113 |
+
"http-body",
|
| 114 |
+
"http-body-util",
|
| 115 |
+
"hyper",
|
| 116 |
+
"hyper-util",
|
| 117 |
+
"itoa",
|
| 118 |
+
"matchit",
|
| 119 |
+
"memchr",
|
| 120 |
+
"mime",
|
| 121 |
+
"percent-encoding",
|
| 122 |
+
"pin-project-lite",
|
| 123 |
+
"rustversion",
|
| 124 |
+
"serde",
|
| 125 |
+
"serde_json",
|
| 126 |
+
"serde_path_to_error",
|
| 127 |
+
"serde_urlencoded",
|
| 128 |
+
"sync_wrapper",
|
| 129 |
+
"tokio",
|
| 130 |
+
"tower",
|
| 131 |
+
"tower-layer",
|
| 132 |
+
"tower-service",
|
| 133 |
+
"tracing",
|
| 134 |
+
]
|
| 135 |
+
|
| 136 |
+
[[package]]
|
| 137 |
+
name = "axum-core"
|
| 138 |
+
version = "0.5.0"
|
| 139 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 140 |
+
checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
|
| 141 |
+
dependencies = [
|
| 142 |
+
"bytes",
|
| 143 |
+
"futures-util",
|
| 144 |
+
"http",
|
| 145 |
+
"http-body",
|
| 146 |
+
"http-body-util",
|
| 147 |
+
"mime",
|
| 148 |
+
"pin-project-lite",
|
| 149 |
+
"rustversion",
|
| 150 |
+
"sync_wrapper",
|
| 151 |
+
"tower-layer",
|
| 152 |
+
"tower-service",
|
| 153 |
+
"tracing",
|
| 154 |
+
]
|
| 155 |
+
|
| 156 |
+
[[package]]
|
| 157 |
+
name = "backtrace"
|
| 158 |
+
version = "0.3.74"
|
| 159 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 160 |
+
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
| 161 |
+
dependencies = [
|
| 162 |
+
"addr2line",
|
| 163 |
+
"cfg-if",
|
| 164 |
+
"libc",
|
| 165 |
+
"miniz_oxide",
|
| 166 |
+
"object",
|
| 167 |
+
"rustc-demangle",
|
| 168 |
+
"windows-targets",
|
| 169 |
+
]
|
| 170 |
+
|
| 171 |
+
[[package]]
|
| 172 |
+
name = "base64"
|
| 173 |
+
version = "0.22.1"
|
| 174 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 175 |
+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
| 176 |
+
|
| 177 |
+
[[package]]
|
| 178 |
+
name = "bitflags"
|
| 179 |
+
version = "1.3.2"
|
| 180 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 181 |
+
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
| 182 |
+
|
| 183 |
+
[[package]]
|
| 184 |
+
name = "bitflags"
|
| 185 |
+
version = "2.8.0"
|
| 186 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 187 |
+
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
| 188 |
+
|
| 189 |
+
[[package]]
|
| 190 |
+
name = "bitvec"
|
| 191 |
+
version = "1.0.1"
|
| 192 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 193 |
+
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
| 194 |
+
dependencies = [
|
| 195 |
+
"funty",
|
| 196 |
+
"radium",
|
| 197 |
+
"tap",
|
| 198 |
+
"wyz",
|
| 199 |
+
]
|
| 200 |
+
|
| 201 |
+
[[package]]
|
| 202 |
+
name = "block-buffer"
|
| 203 |
+
version = "0.10.4"
|
| 204 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 205 |
+
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
| 206 |
+
dependencies = [
|
| 207 |
+
"generic-array",
|
| 208 |
+
]
|
| 209 |
+
|
| 210 |
+
[[package]]
|
| 211 |
+
name = "brotli"
|
| 212 |
+
version = "7.0.0"
|
| 213 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 214 |
+
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
|
| 215 |
+
dependencies = [
|
| 216 |
+
"alloc-no-stdlib",
|
| 217 |
+
"alloc-stdlib",
|
| 218 |
+
"brotli-decompressor",
|
| 219 |
+
]
|
| 220 |
+
|
| 221 |
+
[[package]]
|
| 222 |
+
name = "brotli-decompressor"
|
| 223 |
+
version = "4.0.1"
|
| 224 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 225 |
+
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
|
| 226 |
+
dependencies = [
|
| 227 |
+
"alloc-no-stdlib",
|
| 228 |
+
"alloc-stdlib",
|
| 229 |
+
]
|
| 230 |
+
|
| 231 |
+
[[package]]
|
| 232 |
+
name = "bumpalo"
|
| 233 |
+
version = "3.16.0"
|
| 234 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 235 |
+
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
| 236 |
+
|
| 237 |
+
[[package]]
|
| 238 |
+
name = "bytecheck"
|
| 239 |
+
version = "0.6.12"
|
| 240 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 241 |
+
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
|
| 242 |
+
dependencies = [
|
| 243 |
+
"bytecheck_derive",
|
| 244 |
+
"ptr_meta 0.1.4",
|
| 245 |
+
"simdutf8",
|
| 246 |
+
]
|
| 247 |
+
|
| 248 |
+
[[package]]
|
| 249 |
+
name = "bytecheck_derive"
|
| 250 |
+
version = "0.6.12"
|
| 251 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 252 |
+
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
| 253 |
+
dependencies = [
|
| 254 |
+
"proc-macro2",
|
| 255 |
+
"quote",
|
| 256 |
+
"syn 1.0.109",
|
| 257 |
+
]
|
| 258 |
+
|
| 259 |
+
[[package]]
|
| 260 |
+
name = "bytemuck"
|
| 261 |
+
version = "1.21.0"
|
| 262 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 263 |
+
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
| 264 |
+
|
| 265 |
+
[[package]]
|
| 266 |
+
name = "byteorder"
|
| 267 |
+
version = "1.5.0"
|
| 268 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 269 |
+
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
| 270 |
+
|
| 271 |
+
[[package]]
|
| 272 |
+
name = "byteorder-lite"
|
| 273 |
+
version = "0.1.0"
|
| 274 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 275 |
+
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
| 276 |
+
|
| 277 |
+
[[package]]
|
| 278 |
+
name = "bytes"
|
| 279 |
+
version = "1.9.0"
|
| 280 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 281 |
+
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
| 282 |
+
|
| 283 |
+
[[package]]
|
| 284 |
+
name = "cc"
|
| 285 |
+
version = "1.2.10"
|
| 286 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 287 |
+
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
| 288 |
+
dependencies = [
|
| 289 |
+
"shlex",
|
| 290 |
+
]
|
| 291 |
+
|
| 292 |
+
[[package]]
|
| 293 |
+
name = "cfg-if"
|
| 294 |
+
version = "1.0.0"
|
| 295 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 296 |
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
| 297 |
+
|
| 298 |
+
[[package]]
|
| 299 |
+
name = "chrono"
|
| 300 |
+
version = "0.4.39"
|
| 301 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 302 |
+
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
| 303 |
+
dependencies = [
|
| 304 |
+
"android-tzdata",
|
| 305 |
+
"iana-time-zone",
|
| 306 |
+
"num-traits",
|
| 307 |
+
"rkyv 0.7.45",
|
| 308 |
+
"serde",
|
| 309 |
+
"windows-targets",
|
| 310 |
+
]
|
| 311 |
+
|
| 312 |
+
[[package]]
|
| 313 |
+
name = "color_quant"
|
| 314 |
+
version = "1.1.0"
|
| 315 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 316 |
+
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
| 317 |
+
|
| 318 |
+
[[package]]
|
| 319 |
+
name = "core-foundation"
|
| 320 |
+
version = "0.9.4"
|
| 321 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 322 |
+
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
| 323 |
+
dependencies = [
|
| 324 |
+
"core-foundation-sys",
|
| 325 |
+
"libc",
|
| 326 |
+
]
|
| 327 |
+
|
| 328 |
+
[[package]]
|
| 329 |
+
name = "core-foundation-sys"
|
| 330 |
+
version = "0.8.7"
|
| 331 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 332 |
+
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
| 333 |
+
|
| 334 |
+
[[package]]
|
| 335 |
+
name = "cpufeatures"
|
| 336 |
+
version = "0.2.16"
|
| 337 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 338 |
+
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
| 339 |
+
dependencies = [
|
| 340 |
+
"libc",
|
| 341 |
+
]
|
| 342 |
+
|
| 343 |
+
[[package]]
|
| 344 |
+
name = "crc32fast"
|
| 345 |
+
version = "1.4.2"
|
| 346 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 347 |
+
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
| 348 |
+
dependencies = [
|
| 349 |
+
"cfg-if",
|
| 350 |
+
]
|
| 351 |
+
|
| 352 |
+
[[package]]
|
| 353 |
+
name = "crypto-common"
|
| 354 |
+
version = "0.1.6"
|
| 355 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 356 |
+
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
| 357 |
+
dependencies = [
|
| 358 |
+
"generic-array",
|
| 359 |
+
"typenum",
|
| 360 |
+
]
|
| 361 |
+
|
| 362 |
+
[[package]]
|
| 363 |
+
name = "cursor-api"
|
| 364 |
+
version = "0.1.3-rc.4.3"
|
| 365 |
+
dependencies = [
|
| 366 |
+
"axum",
|
| 367 |
+
"base64",
|
| 368 |
+
"bytes",
|
| 369 |
+
"chrono",
|
| 370 |
+
"dotenvy",
|
| 371 |
+
"flate2",
|
| 372 |
+
"futures",
|
| 373 |
+
"gif",
|
| 374 |
+
"hex",
|
| 375 |
+
"image",
|
| 376 |
+
"memmap2",
|
| 377 |
+
"parking_lot",
|
| 378 |
+
"paste",
|
| 379 |
+
"prost",
|
| 380 |
+
"prost-build",
|
| 381 |
+
"rand",
|
| 382 |
+
"regex",
|
| 383 |
+
"reqwest",
|
| 384 |
+
"rkyv 0.7.45",
|
| 385 |
+
"serde",
|
| 386 |
+
"serde_json",
|
| 387 |
+
"sha2",
|
| 388 |
+
"sonic-rs",
|
| 389 |
+
"sysinfo",
|
| 390 |
+
"tokio",
|
| 391 |
+
"tokio-stream",
|
| 392 |
+
"tower-http",
|
| 393 |
+
"url",
|
| 394 |
+
"uuid",
|
| 395 |
+
]
|
| 396 |
+
|
| 397 |
+
[[package]]
|
| 398 |
+
name = "digest"
|
| 399 |
+
version = "0.10.7"
|
| 400 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 401 |
+
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
| 402 |
+
dependencies = [
|
| 403 |
+
"block-buffer",
|
| 404 |
+
"crypto-common",
|
| 405 |
+
]
|
| 406 |
+
|
| 407 |
+
[[package]]
|
| 408 |
+
name = "displaydoc"
|
| 409 |
+
version = "0.2.5"
|
| 410 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 411 |
+
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
| 412 |
+
dependencies = [
|
| 413 |
+
"proc-macro2",
|
| 414 |
+
"quote",
|
| 415 |
+
"syn 2.0.96",
|
| 416 |
+
]
|
| 417 |
+
|
| 418 |
+
[[package]]
|
| 419 |
+
name = "dotenvy"
|
| 420 |
+
version = "0.15.7"
|
| 421 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 422 |
+
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
| 423 |
+
|
| 424 |
+
[[package]]
|
| 425 |
+
name = "either"
|
| 426 |
+
version = "1.13.0"
|
| 427 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 428 |
+
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
| 429 |
+
|
| 430 |
+
[[package]]
|
| 431 |
+
name = "encoding_rs"
|
| 432 |
+
version = "0.8.35"
|
| 433 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 434 |
+
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
| 435 |
+
dependencies = [
|
| 436 |
+
"cfg-if",
|
| 437 |
+
]
|
| 438 |
+
|
| 439 |
+
[[package]]
|
| 440 |
+
name = "equivalent"
|
| 441 |
+
version = "1.0.1"
|
| 442 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 443 |
+
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
| 444 |
+
|
| 445 |
+
[[package]]
|
| 446 |
+
name = "errno"
|
| 447 |
+
version = "0.3.10"
|
| 448 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 449 |
+
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
| 450 |
+
dependencies = [
|
| 451 |
+
"libc",
|
| 452 |
+
"windows-sys 0.59.0",
|
| 453 |
+
]
|
| 454 |
+
|
| 455 |
+
[[package]]
|
| 456 |
+
name = "fastrand"
|
| 457 |
+
version = "2.3.0"
|
| 458 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 459 |
+
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
| 460 |
+
|
| 461 |
+
[[package]]
|
| 462 |
+
name = "faststr"
|
| 463 |
+
version = "0.2.27"
|
| 464 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 465 |
+
checksum = "9154486833a83cb5d99de8c4d831314b8ae810dd4ef18d89ceb7a9c7c728dd74"
|
| 466 |
+
dependencies = [
|
| 467 |
+
"bytes",
|
| 468 |
+
"rkyv 0.8.10",
|
| 469 |
+
"serde",
|
| 470 |
+
"simdutf8",
|
| 471 |
+
]
|
| 472 |
+
|
| 473 |
+
[[package]]
|
| 474 |
+
name = "fdeflate"
|
| 475 |
+
version = "0.3.7"
|
| 476 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 477 |
+
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
| 478 |
+
dependencies = [
|
| 479 |
+
"simd-adler32",
|
| 480 |
+
]
|
| 481 |
+
|
| 482 |
+
[[package]]
|
| 483 |
+
name = "fixedbitset"
|
| 484 |
+
version = "0.4.2"
|
| 485 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 486 |
+
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
| 487 |
+
|
| 488 |
+
[[package]]
|
| 489 |
+
name = "flate2"
|
| 490 |
+
version = "1.0.35"
|
| 491 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 492 |
+
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
| 493 |
+
dependencies = [
|
| 494 |
+
"crc32fast",
|
| 495 |
+
"miniz_oxide",
|
| 496 |
+
]
|
| 497 |
+
|
| 498 |
+
[[package]]
|
| 499 |
+
name = "fnv"
|
| 500 |
+
version = "1.0.7"
|
| 501 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 502 |
+
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
| 503 |
+
|
| 504 |
+
[[package]]
|
| 505 |
+
name = "foreign-types"
|
| 506 |
+
version = "0.3.2"
|
| 507 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 508 |
+
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
| 509 |
+
dependencies = [
|
| 510 |
+
"foreign-types-shared",
|
| 511 |
+
]
|
| 512 |
+
|
| 513 |
+
[[package]]
|
| 514 |
+
name = "foreign-types-shared"
|
| 515 |
+
version = "0.1.1"
|
| 516 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 517 |
+
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
| 518 |
+
|
| 519 |
+
[[package]]
|
| 520 |
+
name = "form_urlencoded"
|
| 521 |
+
version = "1.2.1"
|
| 522 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 523 |
+
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
| 524 |
+
dependencies = [
|
| 525 |
+
"percent-encoding",
|
| 526 |
+
]
|
| 527 |
+
|
| 528 |
+
[[package]]
|
| 529 |
+
name = "funty"
|
| 530 |
+
version = "2.0.0"
|
| 531 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 532 |
+
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
| 533 |
+
|
| 534 |
+
[[package]]
|
| 535 |
+
name = "futures"
|
| 536 |
+
version = "0.3.31"
|
| 537 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 538 |
+
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
| 539 |
+
dependencies = [
|
| 540 |
+
"futures-channel",
|
| 541 |
+
"futures-core",
|
| 542 |
+
"futures-io",
|
| 543 |
+
"futures-sink",
|
| 544 |
+
"futures-task",
|
| 545 |
+
"futures-util",
|
| 546 |
+
]
|
| 547 |
+
|
| 548 |
+
[[package]]
|
| 549 |
+
name = "futures-channel"
|
| 550 |
+
version = "0.3.31"
|
| 551 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 552 |
+
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
| 553 |
+
dependencies = [
|
| 554 |
+
"futures-core",
|
| 555 |
+
"futures-sink",
|
| 556 |
+
]
|
| 557 |
+
|
| 558 |
+
[[package]]
|
| 559 |
+
name = "futures-core"
|
| 560 |
+
version = "0.3.31"
|
| 561 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 562 |
+
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
| 563 |
+
|
| 564 |
+
[[package]]
|
| 565 |
+
name = "futures-io"
|
| 566 |
+
version = "0.3.31"
|
| 567 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 568 |
+
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
| 569 |
+
|
| 570 |
+
[[package]]
|
| 571 |
+
name = "futures-macro"
|
| 572 |
+
version = "0.3.31"
|
| 573 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 574 |
+
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
| 575 |
+
dependencies = [
|
| 576 |
+
"proc-macro2",
|
| 577 |
+
"quote",
|
| 578 |
+
"syn 2.0.96",
|
| 579 |
+
]
|
| 580 |
+
|
| 581 |
+
[[package]]
|
| 582 |
+
name = "futures-sink"
|
| 583 |
+
version = "0.3.31"
|
| 584 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 585 |
+
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
| 586 |
+
|
| 587 |
+
[[package]]
|
| 588 |
+
name = "futures-task"
|
| 589 |
+
version = "0.3.31"
|
| 590 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 591 |
+
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
| 592 |
+
|
| 593 |
+
[[package]]
|
| 594 |
+
name = "futures-util"
|
| 595 |
+
version = "0.3.31"
|
| 596 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 597 |
+
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
| 598 |
+
dependencies = [
|
| 599 |
+
"futures-channel",
|
| 600 |
+
"futures-core",
|
| 601 |
+
"futures-io",
|
| 602 |
+
"futures-macro",
|
| 603 |
+
"futures-sink",
|
| 604 |
+
"futures-task",
|
| 605 |
+
"memchr",
|
| 606 |
+
"pin-project-lite",
|
| 607 |
+
"pin-utils",
|
| 608 |
+
"slab",
|
| 609 |
+
]
|
| 610 |
+
|
| 611 |
+
[[package]]
|
| 612 |
+
name = "generic-array"
|
| 613 |
+
version = "0.14.7"
|
| 614 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 615 |
+
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
| 616 |
+
dependencies = [
|
| 617 |
+
"typenum",
|
| 618 |
+
"version_check",
|
| 619 |
+
]
|
| 620 |
+
|
| 621 |
+
[[package]]
|
| 622 |
+
name = "getrandom"
|
| 623 |
+
version = "0.2.15"
|
| 624 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 625 |
+
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
| 626 |
+
dependencies = [
|
| 627 |
+
"cfg-if",
|
| 628 |
+
"libc",
|
| 629 |
+
"wasi",
|
| 630 |
+
]
|
| 631 |
+
|
| 632 |
+
[[package]]
|
| 633 |
+
name = "gif"
|
| 634 |
+
version = "0.13.1"
|
| 635 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 636 |
+
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
|
| 637 |
+
dependencies = [
|
| 638 |
+
"color_quant",
|
| 639 |
+
"weezl",
|
| 640 |
+
]
|
| 641 |
+
|
| 642 |
+
[[package]]
|
| 643 |
+
name = "gimli"
|
| 644 |
+
version = "0.31.1"
|
| 645 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 646 |
+
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
| 647 |
+
|
| 648 |
+
[[package]]
|
| 649 |
+
name = "h2"
|
| 650 |
+
version = "0.4.7"
|
| 651 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 652 |
+
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
|
| 653 |
+
dependencies = [
|
| 654 |
+
"atomic-waker",
|
| 655 |
+
"bytes",
|
| 656 |
+
"fnv",
|
| 657 |
+
"futures-core",
|
| 658 |
+
"futures-sink",
|
| 659 |
+
"http",
|
| 660 |
+
"indexmap",
|
| 661 |
+
"slab",
|
| 662 |
+
"tokio",
|
| 663 |
+
"tokio-util",
|
| 664 |
+
"tracing",
|
| 665 |
+
]
|
| 666 |
+
|
| 667 |
+
[[package]]
|
| 668 |
+
name = "hashbrown"
|
| 669 |
+
version = "0.12.3"
|
| 670 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 671 |
+
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
| 672 |
+
dependencies = [
|
| 673 |
+
"ahash",
|
| 674 |
+
]
|
| 675 |
+
|
| 676 |
+
[[package]]
|
| 677 |
+
name = "hashbrown"
|
| 678 |
+
version = "0.15.2"
|
| 679 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 680 |
+
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
| 681 |
+
|
| 682 |
+
[[package]]
|
| 683 |
+
name = "heck"
|
| 684 |
+
version = "0.5.0"
|
| 685 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 686 |
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
| 687 |
+
|
| 688 |
+
[[package]]
|
| 689 |
+
name = "hex"
|
| 690 |
+
version = "0.4.3"
|
| 691 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 692 |
+
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
| 693 |
+
|
| 694 |
+
[[package]]
|
| 695 |
+
name = "http"
|
| 696 |
+
version = "1.2.0"
|
| 697 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 698 |
+
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
| 699 |
+
dependencies = [
|
| 700 |
+
"bytes",
|
| 701 |
+
"fnv",
|
| 702 |
+
"itoa",
|
| 703 |
+
]
|
| 704 |
+
|
| 705 |
+
[[package]]
|
| 706 |
+
name = "http-body"
|
| 707 |
+
version = "1.0.1"
|
| 708 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 709 |
+
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
| 710 |
+
dependencies = [
|
| 711 |
+
"bytes",
|
| 712 |
+
"http",
|
| 713 |
+
]
|
| 714 |
+
|
| 715 |
+
[[package]]
|
| 716 |
+
name = "http-body-util"
|
| 717 |
+
version = "0.1.2"
|
| 718 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 719 |
+
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
| 720 |
+
dependencies = [
|
| 721 |
+
"bytes",
|
| 722 |
+
"futures-util",
|
| 723 |
+
"http",
|
| 724 |
+
"http-body",
|
| 725 |
+
"pin-project-lite",
|
| 726 |
+
]
|
| 727 |
+
|
| 728 |
+
[[package]]
|
| 729 |
+
name = "httparse"
|
| 730 |
+
version = "1.9.5"
|
| 731 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 732 |
+
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
|
| 733 |
+
|
| 734 |
+
[[package]]
|
| 735 |
+
name = "httpdate"
|
| 736 |
+
version = "1.0.3"
|
| 737 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 738 |
+
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
| 739 |
+
|
| 740 |
+
[[package]]
|
| 741 |
+
name = "hyper"
|
| 742 |
+
version = "1.5.2"
|
| 743 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 744 |
+
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
| 745 |
+
dependencies = [
|
| 746 |
+
"bytes",
|
| 747 |
+
"futures-channel",
|
| 748 |
+
"futures-util",
|
| 749 |
+
"h2",
|
| 750 |
+
"http",
|
| 751 |
+
"http-body",
|
| 752 |
+
"httparse",
|
| 753 |
+
"httpdate",
|
| 754 |
+
"itoa",
|
| 755 |
+
"pin-project-lite",
|
| 756 |
+
"smallvec",
|
| 757 |
+
"tokio",
|
| 758 |
+
"want",
|
| 759 |
+
]
|
| 760 |
+
|
| 761 |
+
[[package]]
|
| 762 |
+
name = "hyper-rustls"
|
| 763 |
+
version = "0.27.5"
|
| 764 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 765 |
+
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
| 766 |
+
dependencies = [
|
| 767 |
+
"futures-util",
|
| 768 |
+
"http",
|
| 769 |
+
"hyper",
|
| 770 |
+
"hyper-util",
|
| 771 |
+
"rustls",
|
| 772 |
+
"rustls-pki-types",
|
| 773 |
+
"tokio",
|
| 774 |
+
"tokio-rustls",
|
| 775 |
+
"tower-service",
|
| 776 |
+
]
|
| 777 |
+
|
| 778 |
+
[[package]]
|
| 779 |
+
name = "hyper-tls"
|
| 780 |
+
version = "0.6.0"
|
| 781 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 782 |
+
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
| 783 |
+
dependencies = [
|
| 784 |
+
"bytes",
|
| 785 |
+
"http-body-util",
|
| 786 |
+
"hyper",
|
| 787 |
+
"hyper-util",
|
| 788 |
+
"native-tls",
|
| 789 |
+
"tokio",
|
| 790 |
+
"tokio-native-tls",
|
| 791 |
+
"tower-service",
|
| 792 |
+
]
|
| 793 |
+
|
| 794 |
+
[[package]]
|
| 795 |
+
name = "hyper-util"
|
| 796 |
+
version = "0.1.10"
|
| 797 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 798 |
+
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
| 799 |
+
dependencies = [
|
| 800 |
+
"bytes",
|
| 801 |
+
"futures-channel",
|
| 802 |
+
"futures-util",
|
| 803 |
+
"http",
|
| 804 |
+
"http-body",
|
| 805 |
+
"hyper",
|
| 806 |
+
"pin-project-lite",
|
| 807 |
+
"socket2",
|
| 808 |
+
"tokio",
|
| 809 |
+
"tower-service",
|
| 810 |
+
"tracing",
|
| 811 |
+
]
|
| 812 |
+
|
| 813 |
+
[[package]]
|
| 814 |
+
name = "iana-time-zone"
|
| 815 |
+
version = "0.1.61"
|
| 816 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 817 |
+
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
| 818 |
+
dependencies = [
|
| 819 |
+
"android_system_properties",
|
| 820 |
+
"core-foundation-sys",
|
| 821 |
+
"iana-time-zone-haiku",
|
| 822 |
+
"js-sys",
|
| 823 |
+
"wasm-bindgen",
|
| 824 |
+
"windows-core 0.52.0",
|
| 825 |
+
]
|
| 826 |
+
|
| 827 |
+
[[package]]
|
| 828 |
+
name = "iana-time-zone-haiku"
|
| 829 |
+
version = "0.1.2"
|
| 830 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 831 |
+
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
| 832 |
+
dependencies = [
|
| 833 |
+
"cc",
|
| 834 |
+
]
|
| 835 |
+
|
| 836 |
+
[[package]]
|
| 837 |
+
name = "icu_collections"
|
| 838 |
+
version = "1.5.0"
|
| 839 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 840 |
+
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
| 841 |
+
dependencies = [
|
| 842 |
+
"displaydoc",
|
| 843 |
+
"yoke",
|
| 844 |
+
"zerofrom",
|
| 845 |
+
"zerovec",
|
| 846 |
+
]
|
| 847 |
+
|
| 848 |
+
[[package]]
|
| 849 |
+
name = "icu_locid"
|
| 850 |
+
version = "1.5.0"
|
| 851 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 852 |
+
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
| 853 |
+
dependencies = [
|
| 854 |
+
"displaydoc",
|
| 855 |
+
"litemap",
|
| 856 |
+
"tinystr",
|
| 857 |
+
"writeable",
|
| 858 |
+
"zerovec",
|
| 859 |
+
]
|
| 860 |
+
|
| 861 |
+
[[package]]
|
| 862 |
+
name = "icu_locid_transform"
|
| 863 |
+
version = "1.5.0"
|
| 864 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 865 |
+
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
| 866 |
+
dependencies = [
|
| 867 |
+
"displaydoc",
|
| 868 |
+
"icu_locid",
|
| 869 |
+
"icu_locid_transform_data",
|
| 870 |
+
"icu_provider",
|
| 871 |
+
"tinystr",
|
| 872 |
+
"zerovec",
|
| 873 |
+
]
|
| 874 |
+
|
| 875 |
+
[[package]]
|
| 876 |
+
name = "icu_locid_transform_data"
|
| 877 |
+
version = "1.5.0"
|
| 878 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 879 |
+
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
| 880 |
+
|
| 881 |
+
[[package]]
|
| 882 |
+
name = "icu_normalizer"
|
| 883 |
+
version = "1.5.0"
|
| 884 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 885 |
+
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
| 886 |
+
dependencies = [
|
| 887 |
+
"displaydoc",
|
| 888 |
+
"icu_collections",
|
| 889 |
+
"icu_normalizer_data",
|
| 890 |
+
"icu_properties",
|
| 891 |
+
"icu_provider",
|
| 892 |
+
"smallvec",
|
| 893 |
+
"utf16_iter",
|
| 894 |
+
"utf8_iter",
|
| 895 |
+
"write16",
|
| 896 |
+
"zerovec",
|
| 897 |
+
]
|
| 898 |
+
|
| 899 |
+
[[package]]
|
| 900 |
+
name = "icu_normalizer_data"
|
| 901 |
+
version = "1.5.0"
|
| 902 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 903 |
+
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
| 904 |
+
|
| 905 |
+
[[package]]
|
| 906 |
+
name = "icu_properties"
|
| 907 |
+
version = "1.5.1"
|
| 908 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 909 |
+
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
| 910 |
+
dependencies = [
|
| 911 |
+
"displaydoc",
|
| 912 |
+
"icu_collections",
|
| 913 |
+
"icu_locid_transform",
|
| 914 |
+
"icu_properties_data",
|
| 915 |
+
"icu_provider",
|
| 916 |
+
"tinystr",
|
| 917 |
+
"zerovec",
|
| 918 |
+
]
|
| 919 |
+
|
| 920 |
+
[[package]]
|
| 921 |
+
name = "icu_properties_data"
|
| 922 |
+
version = "1.5.0"
|
| 923 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 924 |
+
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
| 925 |
+
|
| 926 |
+
[[package]]
|
| 927 |
+
name = "icu_provider"
|
| 928 |
+
version = "1.5.0"
|
| 929 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 930 |
+
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
| 931 |
+
dependencies = [
|
| 932 |
+
"displaydoc",
|
| 933 |
+
"icu_locid",
|
| 934 |
+
"icu_provider_macros",
|
| 935 |
+
"stable_deref_trait",
|
| 936 |
+
"tinystr",
|
| 937 |
+
"writeable",
|
| 938 |
+
"yoke",
|
| 939 |
+
"zerofrom",
|
| 940 |
+
"zerovec",
|
| 941 |
+
]
|
| 942 |
+
|
| 943 |
+
[[package]]
|
| 944 |
+
name = "icu_provider_macros"
|
| 945 |
+
version = "1.5.0"
|
| 946 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 947 |
+
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
| 948 |
+
dependencies = [
|
| 949 |
+
"proc-macro2",
|
| 950 |
+
"quote",
|
| 951 |
+
"syn 2.0.96",
|
| 952 |
+
]
|
| 953 |
+
|
| 954 |
+
[[package]]
|
| 955 |
+
name = "idna"
|
| 956 |
+
version = "1.0.3"
|
| 957 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 958 |
+
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
| 959 |
+
dependencies = [
|
| 960 |
+
"idna_adapter",
|
| 961 |
+
"smallvec",
|
| 962 |
+
"utf8_iter",
|
| 963 |
+
]
|
| 964 |
+
|
| 965 |
+
[[package]]
|
| 966 |
+
name = "idna_adapter"
|
| 967 |
+
version = "1.2.0"
|
| 968 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 969 |
+
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
| 970 |
+
dependencies = [
|
| 971 |
+
"icu_normalizer",
|
| 972 |
+
"icu_properties",
|
| 973 |
+
]
|
| 974 |
+
|
| 975 |
+
[[package]]
|
| 976 |
+
name = "image"
|
| 977 |
+
version = "0.25.5"
|
| 978 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 979 |
+
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
|
| 980 |
+
dependencies = [
|
| 981 |
+
"bytemuck",
|
| 982 |
+
"byteorder-lite",
|
| 983 |
+
"color_quant",
|
| 984 |
+
"gif",
|
| 985 |
+
"image-webp",
|
| 986 |
+
"num-traits",
|
| 987 |
+
"png",
|
| 988 |
+
"zune-core",
|
| 989 |
+
"zune-jpeg",
|
| 990 |
+
]
|
| 991 |
+
|
| 992 |
+
[[package]]
|
| 993 |
+
name = "image-webp"
|
| 994 |
+
version = "0.2.1"
|
| 995 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 996 |
+
checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f"
|
| 997 |
+
dependencies = [
|
| 998 |
+
"byteorder-lite",
|
| 999 |
+
"quick-error",
|
| 1000 |
+
]
|
| 1001 |
+
|
| 1002 |
+
[[package]]
|
| 1003 |
+
name = "indexmap"
|
| 1004 |
+
version = "2.7.1"
|
| 1005 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1006 |
+
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
| 1007 |
+
dependencies = [
|
| 1008 |
+
"equivalent",
|
| 1009 |
+
"hashbrown 0.15.2",
|
| 1010 |
+
]
|
| 1011 |
+
|
| 1012 |
+
[[package]]
|
| 1013 |
+
name = "ipnet"
|
| 1014 |
+
version = "2.11.0"
|
| 1015 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1016 |
+
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
| 1017 |
+
|
| 1018 |
+
[[package]]
|
| 1019 |
+
name = "itertools"
|
| 1020 |
+
version = "0.13.0"
|
| 1021 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1022 |
+
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
| 1023 |
+
dependencies = [
|
| 1024 |
+
"either",
|
| 1025 |
+
]
|
| 1026 |
+
|
| 1027 |
+
[[package]]
|
| 1028 |
+
name = "itoa"
|
| 1029 |
+
version = "1.0.14"
|
| 1030 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1031 |
+
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
| 1032 |
+
|
| 1033 |
+
[[package]]
|
| 1034 |
+
name = "js-sys"
|
| 1035 |
+
version = "0.3.77"
|
| 1036 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1037 |
+
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
| 1038 |
+
dependencies = [
|
| 1039 |
+
"once_cell",
|
| 1040 |
+
"wasm-bindgen",
|
| 1041 |
+
]
|
| 1042 |
+
|
| 1043 |
+
[[package]]
|
| 1044 |
+
name = "libc"
|
| 1045 |
+
version = "0.2.169"
|
| 1046 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1047 |
+
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
| 1048 |
+
|
| 1049 |
+
[[package]]
|
| 1050 |
+
name = "linux-raw-sys"
|
| 1051 |
+
version = "0.4.15"
|
| 1052 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1053 |
+
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
| 1054 |
+
|
| 1055 |
+
[[package]]
|
| 1056 |
+
name = "litemap"
|
| 1057 |
+
version = "0.7.4"
|
| 1058 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1059 |
+
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
| 1060 |
+
|
| 1061 |
+
[[package]]
|
| 1062 |
+
name = "lock_api"
|
| 1063 |
+
version = "0.4.12"
|
| 1064 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1065 |
+
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
| 1066 |
+
dependencies = [
|
| 1067 |
+
"autocfg",
|
| 1068 |
+
"scopeguard",
|
| 1069 |
+
]
|
| 1070 |
+
|
| 1071 |
+
[[package]]
|
| 1072 |
+
name = "log"
|
| 1073 |
+
version = "0.4.25"
|
| 1074 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1075 |
+
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
| 1076 |
+
|
| 1077 |
+
[[package]]
|
| 1078 |
+
name = "matchit"
|
| 1079 |
+
version = "0.8.4"
|
| 1080 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1081 |
+
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
| 1082 |
+
|
| 1083 |
+
[[package]]
|
| 1084 |
+
name = "memchr"
|
| 1085 |
+
version = "2.7.4"
|
| 1086 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1087 |
+
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
| 1088 |
+
|
| 1089 |
+
[[package]]
|
| 1090 |
+
name = "memmap2"
|
| 1091 |
+
version = "0.9.5"
|
| 1092 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1093 |
+
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
| 1094 |
+
dependencies = [
|
| 1095 |
+
"libc",
|
| 1096 |
+
]
|
| 1097 |
+
|
| 1098 |
+
[[package]]
|
| 1099 |
+
name = "mime"
|
| 1100 |
+
version = "0.3.17"
|
| 1101 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1102 |
+
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
| 1103 |
+
|
| 1104 |
+
[[package]]
|
| 1105 |
+
name = "miniz_oxide"
|
| 1106 |
+
version = "0.8.3"
|
| 1107 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1108 |
+
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
| 1109 |
+
dependencies = [
|
| 1110 |
+
"adler2",
|
| 1111 |
+
"simd-adler32",
|
| 1112 |
+
]
|
| 1113 |
+
|
| 1114 |
+
[[package]]
|
| 1115 |
+
name = "mio"
|
| 1116 |
+
version = "1.0.3"
|
| 1117 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1118 |
+
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
| 1119 |
+
dependencies = [
|
| 1120 |
+
"libc",
|
| 1121 |
+
"wasi",
|
| 1122 |
+
"windows-sys 0.52.0",
|
| 1123 |
+
]
|
| 1124 |
+
|
| 1125 |
+
[[package]]
|
| 1126 |
+
name = "multimap"
|
| 1127 |
+
version = "0.10.0"
|
| 1128 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1129 |
+
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
|
| 1130 |
+
|
| 1131 |
+
[[package]]
|
| 1132 |
+
name = "munge"
|
| 1133 |
+
version = "0.4.1"
|
| 1134 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1135 |
+
checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df"
|
| 1136 |
+
dependencies = [
|
| 1137 |
+
"munge_macro",
|
| 1138 |
+
]
|
| 1139 |
+
|
| 1140 |
+
[[package]]
|
| 1141 |
+
name = "munge_macro"
|
| 1142 |
+
version = "0.4.1"
|
| 1143 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1144 |
+
checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e"
|
| 1145 |
+
dependencies = [
|
| 1146 |
+
"proc-macro2",
|
| 1147 |
+
"quote",
|
| 1148 |
+
"syn 2.0.96",
|
| 1149 |
+
]
|
| 1150 |
+
|
| 1151 |
+
[[package]]
|
| 1152 |
+
name = "native-tls"
|
| 1153 |
+
version = "0.2.12"
|
| 1154 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1155 |
+
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
|
| 1156 |
+
dependencies = [
|
| 1157 |
+
"libc",
|
| 1158 |
+
"log",
|
| 1159 |
+
"openssl",
|
| 1160 |
+
"openssl-probe",
|
| 1161 |
+
"openssl-sys",
|
| 1162 |
+
"schannel",
|
| 1163 |
+
"security-framework",
|
| 1164 |
+
"security-framework-sys",
|
| 1165 |
+
"tempfile",
|
| 1166 |
+
]
|
| 1167 |
+
|
| 1168 |
+
[[package]]
|
| 1169 |
+
name = "ntapi"
|
| 1170 |
+
version = "0.4.1"
|
| 1171 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1172 |
+
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
| 1173 |
+
dependencies = [
|
| 1174 |
+
"winapi",
|
| 1175 |
+
]
|
| 1176 |
+
|
| 1177 |
+
[[package]]
|
| 1178 |
+
name = "num-traits"
|
| 1179 |
+
version = "0.2.19"
|
| 1180 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1181 |
+
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
| 1182 |
+
dependencies = [
|
| 1183 |
+
"autocfg",
|
| 1184 |
+
]
|
| 1185 |
+
|
| 1186 |
+
[[package]]
|
| 1187 |
+
name = "object"
|
| 1188 |
+
version = "0.36.7"
|
| 1189 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1190 |
+
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
| 1191 |
+
dependencies = [
|
| 1192 |
+
"memchr",
|
| 1193 |
+
]
|
| 1194 |
+
|
| 1195 |
+
[[package]]
|
| 1196 |
+
name = "once_cell"
|
| 1197 |
+
version = "1.20.2"
|
| 1198 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1199 |
+
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
| 1200 |
+
|
| 1201 |
+
[[package]]
|
| 1202 |
+
name = "openssl"
|
| 1203 |
+
version = "0.10.68"
|
| 1204 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1205 |
+
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
| 1206 |
+
dependencies = [
|
| 1207 |
+
"bitflags 2.8.0",
|
| 1208 |
+
"cfg-if",
|
| 1209 |
+
"foreign-types",
|
| 1210 |
+
"libc",
|
| 1211 |
+
"once_cell",
|
| 1212 |
+
"openssl-macros",
|
| 1213 |
+
"openssl-sys",
|
| 1214 |
+
]
|
| 1215 |
+
|
| 1216 |
+
[[package]]
|
| 1217 |
+
name = "openssl-macros"
|
| 1218 |
+
version = "0.1.1"
|
| 1219 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1220 |
+
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
| 1221 |
+
dependencies = [
|
| 1222 |
+
"proc-macro2",
|
| 1223 |
+
"quote",
|
| 1224 |
+
"syn 2.0.96",
|
| 1225 |
+
]
|
| 1226 |
+
|
| 1227 |
+
[[package]]
|
| 1228 |
+
name = "openssl-probe"
|
| 1229 |
+
version = "0.1.5"
|
| 1230 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1231 |
+
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
| 1232 |
+
|
| 1233 |
+
[[package]]
|
| 1234 |
+
name = "openssl-sys"
|
| 1235 |
+
version = "0.9.104"
|
| 1236 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1237 |
+
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
| 1238 |
+
dependencies = [
|
| 1239 |
+
"cc",
|
| 1240 |
+
"libc",
|
| 1241 |
+
"pkg-config",
|
| 1242 |
+
"vcpkg",
|
| 1243 |
+
]
|
| 1244 |
+
|
| 1245 |
+
[[package]]
|
| 1246 |
+
name = "parking_lot"
|
| 1247 |
+
version = "0.12.3"
|
| 1248 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1249 |
+
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
| 1250 |
+
dependencies = [
|
| 1251 |
+
"lock_api",
|
| 1252 |
+
"parking_lot_core",
|
| 1253 |
+
]
|
| 1254 |
+
|
| 1255 |
+
[[package]]
|
| 1256 |
+
name = "parking_lot_core"
|
| 1257 |
+
version = "0.9.10"
|
| 1258 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1259 |
+
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
| 1260 |
+
dependencies = [
|
| 1261 |
+
"cfg-if",
|
| 1262 |
+
"libc",
|
| 1263 |
+
"redox_syscall",
|
| 1264 |
+
"smallvec",
|
| 1265 |
+
"windows-targets",
|
| 1266 |
+
]
|
| 1267 |
+
|
| 1268 |
+
[[package]]
|
| 1269 |
+
name = "paste"
|
| 1270 |
+
version = "1.0.15"
|
| 1271 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1272 |
+
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
| 1273 |
+
|
| 1274 |
+
[[package]]
|
| 1275 |
+
name = "percent-encoding"
|
| 1276 |
+
version = "2.3.1"
|
| 1277 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1278 |
+
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
| 1279 |
+
|
| 1280 |
+
[[package]]
|
| 1281 |
+
name = "petgraph"
|
| 1282 |
+
version = "0.6.5"
|
| 1283 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1284 |
+
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
| 1285 |
+
dependencies = [
|
| 1286 |
+
"fixedbitset",
|
| 1287 |
+
"indexmap",
|
| 1288 |
+
]
|
| 1289 |
+
|
| 1290 |
+
[[package]]
|
| 1291 |
+
name = "pin-project-lite"
|
| 1292 |
+
version = "0.2.16"
|
| 1293 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1294 |
+
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
| 1295 |
+
|
| 1296 |
+
[[package]]
|
| 1297 |
+
name = "pin-utils"
|
| 1298 |
+
version = "0.1.0"
|
| 1299 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1300 |
+
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
| 1301 |
+
|
| 1302 |
+
[[package]]
|
| 1303 |
+
name = "pkg-config"
|
| 1304 |
+
version = "0.3.31"
|
| 1305 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1306 |
+
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
| 1307 |
+
|
| 1308 |
+
[[package]]
|
| 1309 |
+
name = "png"
|
| 1310 |
+
version = "0.17.16"
|
| 1311 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1312 |
+
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
| 1313 |
+
dependencies = [
|
| 1314 |
+
"bitflags 1.3.2",
|
| 1315 |
+
"crc32fast",
|
| 1316 |
+
"fdeflate",
|
| 1317 |
+
"flate2",
|
| 1318 |
+
"miniz_oxide",
|
| 1319 |
+
]
|
| 1320 |
+
|
| 1321 |
+
[[package]]
|
| 1322 |
+
name = "ppv-lite86"
|
| 1323 |
+
version = "0.2.20"
|
| 1324 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1325 |
+
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
| 1326 |
+
dependencies = [
|
| 1327 |
+
"zerocopy",
|
| 1328 |
+
]
|
| 1329 |
+
|
| 1330 |
+
[[package]]
|
| 1331 |
+
name = "prettyplease"
|
| 1332 |
+
version = "0.2.29"
|
| 1333 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1334 |
+
checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
|
| 1335 |
+
dependencies = [
|
| 1336 |
+
"proc-macro2",
|
| 1337 |
+
"syn 2.0.96",
|
| 1338 |
+
]
|
| 1339 |
+
|
| 1340 |
+
[[package]]
|
| 1341 |
+
name = "proc-macro2"
|
| 1342 |
+
version = "1.0.93"
|
| 1343 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1344 |
+
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
| 1345 |
+
dependencies = [
|
| 1346 |
+
"unicode-ident",
|
| 1347 |
+
]
|
| 1348 |
+
|
| 1349 |
+
[[package]]
|
| 1350 |
+
name = "prost"
|
| 1351 |
+
version = "0.13.4"
|
| 1352 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1353 |
+
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
|
| 1354 |
+
dependencies = [
|
| 1355 |
+
"bytes",
|
| 1356 |
+
"prost-derive",
|
| 1357 |
+
]
|
| 1358 |
+
|
| 1359 |
+
[[package]]
|
| 1360 |
+
name = "prost-build"
|
| 1361 |
+
version = "0.13.4"
|
| 1362 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1363 |
+
checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b"
|
| 1364 |
+
dependencies = [
|
| 1365 |
+
"heck",
|
| 1366 |
+
"itertools",
|
| 1367 |
+
"log",
|
| 1368 |
+
"multimap",
|
| 1369 |
+
"once_cell",
|
| 1370 |
+
"petgraph",
|
| 1371 |
+
"prettyplease",
|
| 1372 |
+
"prost",
|
| 1373 |
+
"prost-types",
|
| 1374 |
+
"regex",
|
| 1375 |
+
"syn 2.0.96",
|
| 1376 |
+
"tempfile",
|
| 1377 |
+
]
|
| 1378 |
+
|
| 1379 |
+
[[package]]
|
| 1380 |
+
name = "prost-derive"
|
| 1381 |
+
version = "0.13.4"
|
| 1382 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1383 |
+
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
|
| 1384 |
+
dependencies = [
|
| 1385 |
+
"anyhow",
|
| 1386 |
+
"itertools",
|
| 1387 |
+
"proc-macro2",
|
| 1388 |
+
"quote",
|
| 1389 |
+
"syn 2.0.96",
|
| 1390 |
+
]
|
| 1391 |
+
|
| 1392 |
+
[[package]]
|
| 1393 |
+
name = "prost-types"
|
| 1394 |
+
version = "0.13.4"
|
| 1395 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1396 |
+
checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
|
| 1397 |
+
dependencies = [
|
| 1398 |
+
"prost",
|
| 1399 |
+
]
|
| 1400 |
+
|
| 1401 |
+
[[package]]
|
| 1402 |
+
name = "ptr_meta"
|
| 1403 |
+
version = "0.1.4"
|
| 1404 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1405 |
+
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
|
| 1406 |
+
dependencies = [
|
| 1407 |
+
"ptr_meta_derive 0.1.4",
|
| 1408 |
+
]
|
| 1409 |
+
|
| 1410 |
+
[[package]]
|
| 1411 |
+
name = "ptr_meta"
|
| 1412 |
+
version = "0.3.0"
|
| 1413 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1414 |
+
checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90"
|
| 1415 |
+
dependencies = [
|
| 1416 |
+
"ptr_meta_derive 0.3.0",
|
| 1417 |
+
]
|
| 1418 |
+
|
| 1419 |
+
[[package]]
|
| 1420 |
+
name = "ptr_meta_derive"
|
| 1421 |
+
version = "0.1.4"
|
| 1422 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1423 |
+
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
| 1424 |
+
dependencies = [
|
| 1425 |
+
"proc-macro2",
|
| 1426 |
+
"quote",
|
| 1427 |
+
"syn 1.0.109",
|
| 1428 |
+
]
|
| 1429 |
+
|
| 1430 |
+
[[package]]
|
| 1431 |
+
name = "ptr_meta_derive"
|
| 1432 |
+
version = "0.3.0"
|
| 1433 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1434 |
+
checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1"
|
| 1435 |
+
dependencies = [
|
| 1436 |
+
"proc-macro2",
|
| 1437 |
+
"quote",
|
| 1438 |
+
"syn 2.0.96",
|
| 1439 |
+
]
|
| 1440 |
+
|
| 1441 |
+
[[package]]
|
| 1442 |
+
name = "quick-error"
|
| 1443 |
+
version = "2.0.1"
|
| 1444 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1445 |
+
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
| 1446 |
+
|
| 1447 |
+
[[package]]
|
| 1448 |
+
name = "quote"
|
| 1449 |
+
version = "1.0.38"
|
| 1450 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1451 |
+
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
| 1452 |
+
dependencies = [
|
| 1453 |
+
"proc-macro2",
|
| 1454 |
+
]
|
| 1455 |
+
|
| 1456 |
+
[[package]]
|
| 1457 |
+
name = "radium"
|
| 1458 |
+
version = "0.7.0"
|
| 1459 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1460 |
+
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
| 1461 |
+
|
| 1462 |
+
[[package]]
|
| 1463 |
+
name = "rancor"
|
| 1464 |
+
version = "0.1.0"
|
| 1465 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1466 |
+
checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947"
|
| 1467 |
+
dependencies = [
|
| 1468 |
+
"ptr_meta 0.3.0",
|
| 1469 |
+
]
|
| 1470 |
+
|
| 1471 |
+
[[package]]
|
| 1472 |
+
name = "rand"
|
| 1473 |
+
version = "0.8.5"
|
| 1474 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1475 |
+
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
| 1476 |
+
dependencies = [
|
| 1477 |
+
"libc",
|
| 1478 |
+
"rand_chacha",
|
| 1479 |
+
"rand_core",
|
| 1480 |
+
]
|
| 1481 |
+
|
| 1482 |
+
[[package]]
|
| 1483 |
+
name = "rand_chacha"
|
| 1484 |
+
version = "0.3.1"
|
| 1485 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1486 |
+
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
| 1487 |
+
dependencies = [
|
| 1488 |
+
"ppv-lite86",
|
| 1489 |
+
"rand_core",
|
| 1490 |
+
]
|
| 1491 |
+
|
| 1492 |
+
[[package]]
|
| 1493 |
+
name = "rand_core"
|
| 1494 |
+
version = "0.6.4"
|
| 1495 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1496 |
+
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
| 1497 |
+
dependencies = [
|
| 1498 |
+
"getrandom",
|
| 1499 |
+
]
|
| 1500 |
+
|
| 1501 |
+
[[package]]
|
| 1502 |
+
name = "redox_syscall"
|
| 1503 |
+
version = "0.5.8"
|
| 1504 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1505 |
+
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
| 1506 |
+
dependencies = [
|
| 1507 |
+
"bitflags 2.8.0",
|
| 1508 |
+
]
|
| 1509 |
+
|
| 1510 |
+
[[package]]
|
| 1511 |
+
name = "ref-cast"
|
| 1512 |
+
version = "1.0.23"
|
| 1513 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1514 |
+
checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931"
|
| 1515 |
+
dependencies = [
|
| 1516 |
+
"ref-cast-impl",
|
| 1517 |
+
]
|
| 1518 |
+
|
| 1519 |
+
[[package]]
|
| 1520 |
+
name = "ref-cast-impl"
|
| 1521 |
+
version = "1.0.23"
|
| 1522 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1523 |
+
checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
|
| 1524 |
+
dependencies = [
|
| 1525 |
+
"proc-macro2",
|
| 1526 |
+
"quote",
|
| 1527 |
+
"syn 2.0.96",
|
| 1528 |
+
]
|
| 1529 |
+
|
| 1530 |
+
[[package]]
|
| 1531 |
+
name = "regex"
|
| 1532 |
+
version = "1.11.1"
|
| 1533 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1534 |
+
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
| 1535 |
+
dependencies = [
|
| 1536 |
+
"aho-corasick",
|
| 1537 |
+
"memchr",
|
| 1538 |
+
"regex-automata",
|
| 1539 |
+
"regex-syntax",
|
| 1540 |
+
]
|
| 1541 |
+
|
| 1542 |
+
[[package]]
|
| 1543 |
+
name = "regex-automata"
|
| 1544 |
+
version = "0.4.9"
|
| 1545 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1546 |
+
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
| 1547 |
+
dependencies = [
|
| 1548 |
+
"aho-corasick",
|
| 1549 |
+
"memchr",
|
| 1550 |
+
"regex-syntax",
|
| 1551 |
+
]
|
| 1552 |
+
|
| 1553 |
+
[[package]]
|
| 1554 |
+
name = "regex-syntax"
|
| 1555 |
+
version = "0.8.5"
|
| 1556 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1557 |
+
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
| 1558 |
+
|
| 1559 |
+
[[package]]
|
| 1560 |
+
name = "rend"
|
| 1561 |
+
version = "0.4.2"
|
| 1562 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1563 |
+
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
|
| 1564 |
+
dependencies = [
|
| 1565 |
+
"bytecheck",
|
| 1566 |
+
]
|
| 1567 |
+
|
| 1568 |
+
[[package]]
|
| 1569 |
+
name = "rend"
|
| 1570 |
+
version = "0.5.2"
|
| 1571 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1572 |
+
checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215"
|
| 1573 |
+
|
| 1574 |
+
[[package]]
|
| 1575 |
+
name = "reqwest"
|
| 1576 |
+
version = "0.12.12"
|
| 1577 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1578 |
+
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
| 1579 |
+
dependencies = [
|
| 1580 |
+
"async-compression",
|
| 1581 |
+
"base64",
|
| 1582 |
+
"bytes",
|
| 1583 |
+
"encoding_rs",
|
| 1584 |
+
"futures-core",
|
| 1585 |
+
"futures-util",
|
| 1586 |
+
"h2",
|
| 1587 |
+
"http",
|
| 1588 |
+
"http-body",
|
| 1589 |
+
"http-body-util",
|
| 1590 |
+
"hyper",
|
| 1591 |
+
"hyper-rustls",
|
| 1592 |
+
"hyper-tls",
|
| 1593 |
+
"hyper-util",
|
| 1594 |
+
"ipnet",
|
| 1595 |
+
"js-sys",
|
| 1596 |
+
"log",
|
| 1597 |
+
"mime",
|
| 1598 |
+
"native-tls",
|
| 1599 |
+
"once_cell",
|
| 1600 |
+
"percent-encoding",
|
| 1601 |
+
"pin-project-lite",
|
| 1602 |
+
"rustls-pemfile",
|
| 1603 |
+
"serde",
|
| 1604 |
+
"serde_json",
|
| 1605 |
+
"serde_urlencoded",
|
| 1606 |
+
"sync_wrapper",
|
| 1607 |
+
"system-configuration",
|
| 1608 |
+
"tokio",
|
| 1609 |
+
"tokio-native-tls",
|
| 1610 |
+
"tokio-socks",
|
| 1611 |
+
"tokio-util",
|
| 1612 |
+
"tower",
|
| 1613 |
+
"tower-service",
|
| 1614 |
+
"url",
|
| 1615 |
+
"wasm-bindgen",
|
| 1616 |
+
"wasm-bindgen-futures",
|
| 1617 |
+
"wasm-streams",
|
| 1618 |
+
"web-sys",
|
| 1619 |
+
"windows-registry",
|
| 1620 |
+
]
|
| 1621 |
+
|
| 1622 |
+
[[package]]
|
| 1623 |
+
name = "ring"
|
| 1624 |
+
version = "0.17.8"
|
| 1625 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1626 |
+
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
| 1627 |
+
dependencies = [
|
| 1628 |
+
"cc",
|
| 1629 |
+
"cfg-if",
|
| 1630 |
+
"getrandom",
|
| 1631 |
+
"libc",
|
| 1632 |
+
"spin",
|
| 1633 |
+
"untrusted",
|
| 1634 |
+
"windows-sys 0.52.0",
|
| 1635 |
+
]
|
| 1636 |
+
|
| 1637 |
+
[[package]]
|
| 1638 |
+
name = "rkyv"
|
| 1639 |
+
version = "0.7.45"
|
| 1640 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1641 |
+
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
|
| 1642 |
+
dependencies = [
|
| 1643 |
+
"bitvec",
|
| 1644 |
+
"bytecheck",
|
| 1645 |
+
"bytes",
|
| 1646 |
+
"hashbrown 0.12.3",
|
| 1647 |
+
"ptr_meta 0.1.4",
|
| 1648 |
+
"rend 0.4.2",
|
| 1649 |
+
"rkyv_derive 0.7.45",
|
| 1650 |
+
"seahash",
|
| 1651 |
+
"tinyvec",
|
| 1652 |
+
"uuid",
|
| 1653 |
+
]
|
| 1654 |
+
|
| 1655 |
+
[[package]]
|
| 1656 |
+
name = "rkyv"
|
| 1657 |
+
version = "0.8.10"
|
| 1658 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1659 |
+
checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65"
|
| 1660 |
+
dependencies = [
|
| 1661 |
+
"bytes",
|
| 1662 |
+
"hashbrown 0.15.2",
|
| 1663 |
+
"indexmap",
|
| 1664 |
+
"munge",
|
| 1665 |
+
"ptr_meta 0.3.0",
|
| 1666 |
+
"rancor",
|
| 1667 |
+
"rend 0.5.2",
|
| 1668 |
+
"rkyv_derive 0.8.10",
|
| 1669 |
+
"tinyvec",
|
| 1670 |
+
"uuid",
|
| 1671 |
+
]
|
| 1672 |
+
|
| 1673 |
+
[[package]]
|
| 1674 |
+
name = "rkyv_derive"
|
| 1675 |
+
version = "0.7.45"
|
| 1676 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1677 |
+
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
|
| 1678 |
+
dependencies = [
|
| 1679 |
+
"proc-macro2",
|
| 1680 |
+
"quote",
|
| 1681 |
+
"syn 1.0.109",
|
| 1682 |
+
]
|
| 1683 |
+
|
| 1684 |
+
[[package]]
|
| 1685 |
+
name = "rkyv_derive"
|
| 1686 |
+
version = "0.8.10"
|
| 1687 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1688 |
+
checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a"
|
| 1689 |
+
dependencies = [
|
| 1690 |
+
"proc-macro2",
|
| 1691 |
+
"quote",
|
| 1692 |
+
"syn 2.0.96",
|
| 1693 |
+
]
|
| 1694 |
+
|
| 1695 |
+
[[package]]
|
| 1696 |
+
name = "rustc-demangle"
|
| 1697 |
+
version = "0.1.24"
|
| 1698 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1699 |
+
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
| 1700 |
+
|
| 1701 |
+
[[package]]
|
| 1702 |
+
name = "rustix"
|
| 1703 |
+
version = "0.38.43"
|
| 1704 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1705 |
+
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
| 1706 |
+
dependencies = [
|
| 1707 |
+
"bitflags 2.8.0",
|
| 1708 |
+
"errno",
|
| 1709 |
+
"libc",
|
| 1710 |
+
"linux-raw-sys",
|
| 1711 |
+
"windows-sys 0.59.0",
|
| 1712 |
+
]
|
| 1713 |
+
|
| 1714 |
+
[[package]]
|
| 1715 |
+
name = "rustls"
|
| 1716 |
+
version = "0.23.21"
|
| 1717 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1718 |
+
checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
|
| 1719 |
+
dependencies = [
|
| 1720 |
+
"once_cell",
|
| 1721 |
+
"rustls-pki-types",
|
| 1722 |
+
"rustls-webpki",
|
| 1723 |
+
"subtle",
|
| 1724 |
+
"zeroize",
|
| 1725 |
+
]
|
| 1726 |
+
|
| 1727 |
+
[[package]]
|
| 1728 |
+
name = "rustls-pemfile"
|
| 1729 |
+
version = "2.2.0"
|
| 1730 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1731 |
+
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
| 1732 |
+
dependencies = [
|
| 1733 |
+
"rustls-pki-types",
|
| 1734 |
+
]
|
| 1735 |
+
|
| 1736 |
+
[[package]]
|
| 1737 |
+
name = "rustls-pki-types"
|
| 1738 |
+
version = "1.10.1"
|
| 1739 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1740 |
+
checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
|
| 1741 |
+
|
| 1742 |
+
[[package]]
|
| 1743 |
+
name = "rustls-webpki"
|
| 1744 |
+
version = "0.102.8"
|
| 1745 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1746 |
+
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
| 1747 |
+
dependencies = [
|
| 1748 |
+
"ring",
|
| 1749 |
+
"rustls-pki-types",
|
| 1750 |
+
"untrusted",
|
| 1751 |
+
]
|
| 1752 |
+
|
| 1753 |
+
[[package]]
|
| 1754 |
+
name = "rustversion"
|
| 1755 |
+
version = "1.0.19"
|
| 1756 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1757 |
+
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
| 1758 |
+
|
| 1759 |
+
[[package]]
|
| 1760 |
+
name = "ryu"
|
| 1761 |
+
version = "1.0.18"
|
| 1762 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1763 |
+
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
| 1764 |
+
|
| 1765 |
+
[[package]]
|
| 1766 |
+
name = "schannel"
|
| 1767 |
+
version = "0.1.27"
|
| 1768 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1769 |
+
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
|
| 1770 |
+
dependencies = [
|
| 1771 |
+
"windows-sys 0.59.0",
|
| 1772 |
+
]
|
| 1773 |
+
|
| 1774 |
+
[[package]]
|
| 1775 |
+
name = "scopeguard"
|
| 1776 |
+
version = "1.2.0"
|
| 1777 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1778 |
+
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
| 1779 |
+
|
| 1780 |
+
[[package]]
|
| 1781 |
+
name = "seahash"
|
| 1782 |
+
version = "4.1.0"
|
| 1783 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1784 |
+
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
| 1785 |
+
|
| 1786 |
+
[[package]]
|
| 1787 |
+
name = "security-framework"
|
| 1788 |
+
version = "2.11.1"
|
| 1789 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1790 |
+
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
| 1791 |
+
dependencies = [
|
| 1792 |
+
"bitflags 2.8.0",
|
| 1793 |
+
"core-foundation",
|
| 1794 |
+
"core-foundation-sys",
|
| 1795 |
+
"libc",
|
| 1796 |
+
"security-framework-sys",
|
| 1797 |
+
]
|
| 1798 |
+
|
| 1799 |
+
[[package]]
|
| 1800 |
+
name = "security-framework-sys"
|
| 1801 |
+
version = "2.14.0"
|
| 1802 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1803 |
+
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
|
| 1804 |
+
dependencies = [
|
| 1805 |
+
"core-foundation-sys",
|
| 1806 |
+
"libc",
|
| 1807 |
+
]
|
| 1808 |
+
|
| 1809 |
+
[[package]]
|
| 1810 |
+
name = "serde"
|
| 1811 |
+
version = "1.0.217"
|
| 1812 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1813 |
+
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
| 1814 |
+
dependencies = [
|
| 1815 |
+
"serde_derive",
|
| 1816 |
+
]
|
| 1817 |
+
|
| 1818 |
+
[[package]]
|
| 1819 |
+
name = "serde_derive"
|
| 1820 |
+
version = "1.0.217"
|
| 1821 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1822 |
+
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
| 1823 |
+
dependencies = [
|
| 1824 |
+
"proc-macro2",
|
| 1825 |
+
"quote",
|
| 1826 |
+
"syn 2.0.96",
|
| 1827 |
+
]
|
| 1828 |
+
|
| 1829 |
+
[[package]]
|
| 1830 |
+
name = "serde_json"
|
| 1831 |
+
version = "1.0.137"
|
| 1832 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1833 |
+
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
| 1834 |
+
dependencies = [
|
| 1835 |
+
"itoa",
|
| 1836 |
+
"memchr",
|
| 1837 |
+
"ryu",
|
| 1838 |
+
"serde",
|
| 1839 |
+
]
|
| 1840 |
+
|
| 1841 |
+
[[package]]
|
| 1842 |
+
name = "serde_path_to_error"
|
| 1843 |
+
version = "0.1.16"
|
| 1844 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1845 |
+
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
|
| 1846 |
+
dependencies = [
|
| 1847 |
+
"itoa",
|
| 1848 |
+
"serde",
|
| 1849 |
+
]
|
| 1850 |
+
|
| 1851 |
+
[[package]]
|
| 1852 |
+
name = "serde_urlencoded"
|
| 1853 |
+
version = "0.7.1"
|
| 1854 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1855 |
+
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
| 1856 |
+
dependencies = [
|
| 1857 |
+
"form_urlencoded",
|
| 1858 |
+
"itoa",
|
| 1859 |
+
"ryu",
|
| 1860 |
+
"serde",
|
| 1861 |
+
]
|
| 1862 |
+
|
| 1863 |
+
[[package]]
|
| 1864 |
+
name = "sha2"
|
| 1865 |
+
version = "0.10.8"
|
| 1866 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1867 |
+
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
| 1868 |
+
dependencies = [
|
| 1869 |
+
"cfg-if",
|
| 1870 |
+
"cpufeatures",
|
| 1871 |
+
"digest",
|
| 1872 |
+
]
|
| 1873 |
+
|
| 1874 |
+
[[package]]
|
| 1875 |
+
name = "shlex"
|
| 1876 |
+
version = "1.3.0"
|
| 1877 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1878 |
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
| 1879 |
+
|
| 1880 |
+
[[package]]
|
| 1881 |
+
name = "signal-hook-registry"
|
| 1882 |
+
version = "1.4.2"
|
| 1883 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1884 |
+
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
| 1885 |
+
dependencies = [
|
| 1886 |
+
"libc",
|
| 1887 |
+
]
|
| 1888 |
+
|
| 1889 |
+
[[package]]
|
| 1890 |
+
name = "simd-adler32"
|
| 1891 |
+
version = "0.3.7"
|
| 1892 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1893 |
+
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
| 1894 |
+
|
| 1895 |
+
[[package]]
|
| 1896 |
+
name = "simdutf8"
|
| 1897 |
+
version = "0.1.5"
|
| 1898 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1899 |
+
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
| 1900 |
+
|
| 1901 |
+
[[package]]
|
| 1902 |
+
name = "slab"
|
| 1903 |
+
version = "0.4.9"
|
| 1904 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1905 |
+
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
| 1906 |
+
dependencies = [
|
| 1907 |
+
"autocfg",
|
| 1908 |
+
]
|
| 1909 |
+
|
| 1910 |
+
[[package]]
|
| 1911 |
+
name = "smallvec"
|
| 1912 |
+
version = "1.13.2"
|
| 1913 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1914 |
+
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
| 1915 |
+
|
| 1916 |
+
[[package]]
|
| 1917 |
+
name = "socket2"
|
| 1918 |
+
version = "0.5.8"
|
| 1919 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1920 |
+
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
| 1921 |
+
dependencies = [
|
| 1922 |
+
"libc",
|
| 1923 |
+
"windows-sys 0.52.0",
|
| 1924 |
+
]
|
| 1925 |
+
|
| 1926 |
+
[[package]]
|
| 1927 |
+
name = "sonic-number"
|
| 1928 |
+
version = "0.1.0"
|
| 1929 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1930 |
+
checksum = "a8a74044c092f4f43ca7a6cfd62854cf9fb5ac8502b131347c990bf22bef1dfe"
|
| 1931 |
+
dependencies = [
|
| 1932 |
+
"cfg-if",
|
| 1933 |
+
]
|
| 1934 |
+
|
| 1935 |
+
[[package]]
|
| 1936 |
+
name = "sonic-rs"
|
| 1937 |
+
version = "0.3.17"
|
| 1938 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1939 |
+
checksum = "0275f9f2f07d47556fe60c2759da8bc4be6083b047b491b2d476aa0bfa558eb1"
|
| 1940 |
+
dependencies = [
|
| 1941 |
+
"bumpalo",
|
| 1942 |
+
"bytes",
|
| 1943 |
+
"cfg-if",
|
| 1944 |
+
"faststr",
|
| 1945 |
+
"itoa",
|
| 1946 |
+
"ref-cast",
|
| 1947 |
+
"ryu",
|
| 1948 |
+
"serde",
|
| 1949 |
+
"simdutf8",
|
| 1950 |
+
"sonic-number",
|
| 1951 |
+
"sonic-simd",
|
| 1952 |
+
"thiserror 2.0.11",
|
| 1953 |
+
]
|
| 1954 |
+
|
| 1955 |
+
[[package]]
|
| 1956 |
+
name = "sonic-simd"
|
| 1957 |
+
version = "0.1.0"
|
| 1958 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1959 |
+
checksum = "940a24e82c9a97483ef66cef06b92160a8fa5cd74042c57c10b24d99d169d2fc"
|
| 1960 |
+
dependencies = [
|
| 1961 |
+
"cfg-if",
|
| 1962 |
+
]
|
| 1963 |
+
|
| 1964 |
+
[[package]]
|
| 1965 |
+
name = "spin"
|
| 1966 |
+
version = "0.9.8"
|
| 1967 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1968 |
+
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
| 1969 |
+
|
| 1970 |
+
[[package]]
|
| 1971 |
+
name = "stable_deref_trait"
|
| 1972 |
+
version = "1.2.0"
|
| 1973 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1974 |
+
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
| 1975 |
+
|
| 1976 |
+
[[package]]
|
| 1977 |
+
name = "subtle"
|
| 1978 |
+
version = "2.6.1"
|
| 1979 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1980 |
+
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
| 1981 |
+
|
| 1982 |
+
[[package]]
|
| 1983 |
+
name = "syn"
|
| 1984 |
+
version = "1.0.109"
|
| 1985 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1986 |
+
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
| 1987 |
+
dependencies = [
|
| 1988 |
+
"proc-macro2",
|
| 1989 |
+
"quote",
|
| 1990 |
+
"unicode-ident",
|
| 1991 |
+
]
|
| 1992 |
+
|
| 1993 |
+
[[package]]
|
| 1994 |
+
name = "syn"
|
| 1995 |
+
version = "2.0.96"
|
| 1996 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 1997 |
+
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
| 1998 |
+
dependencies = [
|
| 1999 |
+
"proc-macro2",
|
| 2000 |
+
"quote",
|
| 2001 |
+
"unicode-ident",
|
| 2002 |
+
]
|
| 2003 |
+
|
| 2004 |
+
[[package]]
|
| 2005 |
+
name = "sync_wrapper"
|
| 2006 |
+
version = "1.0.2"
|
| 2007 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2008 |
+
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
| 2009 |
+
dependencies = [
|
| 2010 |
+
"futures-core",
|
| 2011 |
+
]
|
| 2012 |
+
|
| 2013 |
+
[[package]]
|
| 2014 |
+
name = "synstructure"
|
| 2015 |
+
version = "0.13.1"
|
| 2016 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2017 |
+
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
| 2018 |
+
dependencies = [
|
| 2019 |
+
"proc-macro2",
|
| 2020 |
+
"quote",
|
| 2021 |
+
"syn 2.0.96",
|
| 2022 |
+
]
|
| 2023 |
+
|
| 2024 |
+
[[package]]
|
| 2025 |
+
name = "sysinfo"
|
| 2026 |
+
version = "0.33.1"
|
| 2027 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2028 |
+
checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
|
| 2029 |
+
dependencies = [
|
| 2030 |
+
"core-foundation-sys",
|
| 2031 |
+
"libc",
|
| 2032 |
+
"memchr",
|
| 2033 |
+
"ntapi",
|
| 2034 |
+
"windows",
|
| 2035 |
+
]
|
| 2036 |
+
|
| 2037 |
+
[[package]]
|
| 2038 |
+
name = "system-configuration"
|
| 2039 |
+
version = "0.6.1"
|
| 2040 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2041 |
+
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
| 2042 |
+
dependencies = [
|
| 2043 |
+
"bitflags 2.8.0",
|
| 2044 |
+
"core-foundation",
|
| 2045 |
+
"system-configuration-sys",
|
| 2046 |
+
]
|
| 2047 |
+
|
| 2048 |
+
[[package]]
|
| 2049 |
+
name = "system-configuration-sys"
|
| 2050 |
+
version = "0.6.0"
|
| 2051 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2052 |
+
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
| 2053 |
+
dependencies = [
|
| 2054 |
+
"core-foundation-sys",
|
| 2055 |
+
"libc",
|
| 2056 |
+
]
|
| 2057 |
+
|
| 2058 |
+
[[package]]
|
| 2059 |
+
name = "tap"
|
| 2060 |
+
version = "1.0.1"
|
| 2061 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2062 |
+
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
| 2063 |
+
|
| 2064 |
+
[[package]]
|
| 2065 |
+
name = "tempfile"
|
| 2066 |
+
version = "3.15.0"
|
| 2067 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2068 |
+
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
| 2069 |
+
dependencies = [
|
| 2070 |
+
"cfg-if",
|
| 2071 |
+
"fastrand",
|
| 2072 |
+
"getrandom",
|
| 2073 |
+
"once_cell",
|
| 2074 |
+
"rustix",
|
| 2075 |
+
"windows-sys 0.59.0",
|
| 2076 |
+
]
|
| 2077 |
+
|
| 2078 |
+
[[package]]
|
| 2079 |
+
name = "thiserror"
|
| 2080 |
+
version = "1.0.69"
|
| 2081 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2082 |
+
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
| 2083 |
+
dependencies = [
|
| 2084 |
+
"thiserror-impl 1.0.69",
|
| 2085 |
+
]
|
| 2086 |
+
|
| 2087 |
+
[[package]]
|
| 2088 |
+
name = "thiserror"
|
| 2089 |
+
version = "2.0.11"
|
| 2090 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2091 |
+
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
| 2092 |
+
dependencies = [
|
| 2093 |
+
"thiserror-impl 2.0.11",
|
| 2094 |
+
]
|
| 2095 |
+
|
| 2096 |
+
[[package]]
|
| 2097 |
+
name = "thiserror-impl"
|
| 2098 |
+
version = "1.0.69"
|
| 2099 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2100 |
+
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
| 2101 |
+
dependencies = [
|
| 2102 |
+
"proc-macro2",
|
| 2103 |
+
"quote",
|
| 2104 |
+
"syn 2.0.96",
|
| 2105 |
+
]
|
| 2106 |
+
|
| 2107 |
+
[[package]]
|
| 2108 |
+
name = "thiserror-impl"
|
| 2109 |
+
version = "2.0.11"
|
| 2110 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2111 |
+
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
| 2112 |
+
dependencies = [
|
| 2113 |
+
"proc-macro2",
|
| 2114 |
+
"quote",
|
| 2115 |
+
"syn 2.0.96",
|
| 2116 |
+
]
|
| 2117 |
+
|
| 2118 |
+
[[package]]
|
| 2119 |
+
name = "tinystr"
|
| 2120 |
+
version = "0.7.6"
|
| 2121 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2122 |
+
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
| 2123 |
+
dependencies = [
|
| 2124 |
+
"displaydoc",
|
| 2125 |
+
"zerovec",
|
| 2126 |
+
]
|
| 2127 |
+
|
| 2128 |
+
[[package]]
|
| 2129 |
+
name = "tinyvec"
|
| 2130 |
+
version = "1.8.1"
|
| 2131 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2132 |
+
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
|
| 2133 |
+
dependencies = [
|
| 2134 |
+
"tinyvec_macros",
|
| 2135 |
+
]
|
| 2136 |
+
|
| 2137 |
+
[[package]]
|
| 2138 |
+
name = "tinyvec_macros"
|
| 2139 |
+
version = "0.1.1"
|
| 2140 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2141 |
+
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
| 2142 |
+
|
| 2143 |
+
[[package]]
|
| 2144 |
+
name = "tokio"
|
| 2145 |
+
version = "1.43.0"
|
| 2146 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2147 |
+
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
| 2148 |
+
dependencies = [
|
| 2149 |
+
"backtrace",
|
| 2150 |
+
"bytes",
|
| 2151 |
+
"libc",
|
| 2152 |
+
"mio",
|
| 2153 |
+
"pin-project-lite",
|
| 2154 |
+
"signal-hook-registry",
|
| 2155 |
+
"socket2",
|
| 2156 |
+
"tokio-macros",
|
| 2157 |
+
"windows-sys 0.52.0",
|
| 2158 |
+
]
|
| 2159 |
+
|
| 2160 |
+
[[package]]
|
| 2161 |
+
name = "tokio-macros"
|
| 2162 |
+
version = "2.5.0"
|
| 2163 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2164 |
+
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
| 2165 |
+
dependencies = [
|
| 2166 |
+
"proc-macro2",
|
| 2167 |
+
"quote",
|
| 2168 |
+
"syn 2.0.96",
|
| 2169 |
+
]
|
| 2170 |
+
|
| 2171 |
+
[[package]]
|
| 2172 |
+
name = "tokio-native-tls"
|
| 2173 |
+
version = "0.3.1"
|
| 2174 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2175 |
+
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
| 2176 |
+
dependencies = [
|
| 2177 |
+
"native-tls",
|
| 2178 |
+
"tokio",
|
| 2179 |
+
]
|
| 2180 |
+
|
| 2181 |
+
[[package]]
|
| 2182 |
+
name = "tokio-rustls"
|
| 2183 |
+
version = "0.26.1"
|
| 2184 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2185 |
+
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
| 2186 |
+
dependencies = [
|
| 2187 |
+
"rustls",
|
| 2188 |
+
"tokio",
|
| 2189 |
+
]
|
| 2190 |
+
|
| 2191 |
+
[[package]]
|
| 2192 |
+
name = "tokio-socks"
|
| 2193 |
+
version = "0.5.2"
|
| 2194 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2195 |
+
checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
|
| 2196 |
+
dependencies = [
|
| 2197 |
+
"either",
|
| 2198 |
+
"futures-util",
|
| 2199 |
+
"thiserror 1.0.69",
|
| 2200 |
+
"tokio",
|
| 2201 |
+
]
|
| 2202 |
+
|
| 2203 |
+
[[package]]
|
| 2204 |
+
name = "tokio-stream"
|
| 2205 |
+
version = "0.1.17"
|
| 2206 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2207 |
+
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
|
| 2208 |
+
dependencies = [
|
| 2209 |
+
"futures-core",
|
| 2210 |
+
"pin-project-lite",
|
| 2211 |
+
"tokio",
|
| 2212 |
+
]
|
| 2213 |
+
|
| 2214 |
+
[[package]]
|
| 2215 |
+
name = "tokio-util"
|
| 2216 |
+
version = "0.7.13"
|
| 2217 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2218 |
+
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
| 2219 |
+
dependencies = [
|
| 2220 |
+
"bytes",
|
| 2221 |
+
"futures-core",
|
| 2222 |
+
"futures-sink",
|
| 2223 |
+
"pin-project-lite",
|
| 2224 |
+
"tokio",
|
| 2225 |
+
]
|
| 2226 |
+
|
| 2227 |
+
[[package]]
|
| 2228 |
+
name = "tower"
|
| 2229 |
+
version = "0.5.2"
|
| 2230 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2231 |
+
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
| 2232 |
+
dependencies = [
|
| 2233 |
+
"futures-core",
|
| 2234 |
+
"futures-util",
|
| 2235 |
+
"pin-project-lite",
|
| 2236 |
+
"sync_wrapper",
|
| 2237 |
+
"tokio",
|
| 2238 |
+
"tower-layer",
|
| 2239 |
+
"tower-service",
|
| 2240 |
+
"tracing",
|
| 2241 |
+
]
|
| 2242 |
+
|
| 2243 |
+
[[package]]
|
| 2244 |
+
name = "tower-http"
|
| 2245 |
+
version = "0.6.2"
|
| 2246 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2247 |
+
checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
|
| 2248 |
+
dependencies = [
|
| 2249 |
+
"bitflags 2.8.0",
|
| 2250 |
+
"bytes",
|
| 2251 |
+
"http",
|
| 2252 |
+
"http-body",
|
| 2253 |
+
"http-body-util",
|
| 2254 |
+
"pin-project-lite",
|
| 2255 |
+
"tower-layer",
|
| 2256 |
+
"tower-service",
|
| 2257 |
+
]
|
| 2258 |
+
|
| 2259 |
+
[[package]]
|
| 2260 |
+
name = "tower-layer"
|
| 2261 |
+
version = "0.3.3"
|
| 2262 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2263 |
+
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
| 2264 |
+
|
| 2265 |
+
[[package]]
|
| 2266 |
+
name = "tower-service"
|
| 2267 |
+
version = "0.3.3"
|
| 2268 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2269 |
+
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
| 2270 |
+
|
| 2271 |
+
[[package]]
|
| 2272 |
+
name = "tracing"
|
| 2273 |
+
version = "0.1.41"
|
| 2274 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2275 |
+
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
| 2276 |
+
dependencies = [
|
| 2277 |
+
"log",
|
| 2278 |
+
"pin-project-lite",
|
| 2279 |
+
"tracing-core",
|
| 2280 |
+
]
|
| 2281 |
+
|
| 2282 |
+
[[package]]
|
| 2283 |
+
name = "tracing-core"
|
| 2284 |
+
version = "0.1.33"
|
| 2285 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2286 |
+
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
| 2287 |
+
dependencies = [
|
| 2288 |
+
"once_cell",
|
| 2289 |
+
]
|
| 2290 |
+
|
| 2291 |
+
[[package]]
|
| 2292 |
+
name = "try-lock"
|
| 2293 |
+
version = "0.2.5"
|
| 2294 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2295 |
+
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
| 2296 |
+
|
| 2297 |
+
[[package]]
|
| 2298 |
+
name = "typenum"
|
| 2299 |
+
version = "1.17.0"
|
| 2300 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2301 |
+
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
| 2302 |
+
|
| 2303 |
+
[[package]]
|
| 2304 |
+
name = "unicode-ident"
|
| 2305 |
+
version = "1.0.14"
|
| 2306 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2307 |
+
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
| 2308 |
+
|
| 2309 |
+
[[package]]
|
| 2310 |
+
name = "untrusted"
|
| 2311 |
+
version = "0.9.0"
|
| 2312 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2313 |
+
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
| 2314 |
+
|
| 2315 |
+
[[package]]
|
| 2316 |
+
name = "url"
|
| 2317 |
+
version = "2.5.4"
|
| 2318 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2319 |
+
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
| 2320 |
+
dependencies = [
|
| 2321 |
+
"form_urlencoded",
|
| 2322 |
+
"idna",
|
| 2323 |
+
"percent-encoding",
|
| 2324 |
+
]
|
| 2325 |
+
|
| 2326 |
+
[[package]]
|
| 2327 |
+
name = "utf16_iter"
|
| 2328 |
+
version = "1.0.5"
|
| 2329 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2330 |
+
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
| 2331 |
+
|
| 2332 |
+
[[package]]
|
| 2333 |
+
name = "utf8_iter"
|
| 2334 |
+
version = "1.0.4"
|
| 2335 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2336 |
+
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
| 2337 |
+
|
| 2338 |
+
[[package]]
|
| 2339 |
+
name = "uuid"
|
| 2340 |
+
version = "1.12.1"
|
| 2341 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2342 |
+
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
| 2343 |
+
dependencies = [
|
| 2344 |
+
"getrandom",
|
| 2345 |
+
]
|
| 2346 |
+
|
| 2347 |
+
[[package]]
|
| 2348 |
+
name = "vcpkg"
|
| 2349 |
+
version = "0.2.15"
|
| 2350 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2351 |
+
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
| 2352 |
+
|
| 2353 |
+
[[package]]
|
| 2354 |
+
name = "version_check"
|
| 2355 |
+
version = "0.9.5"
|
| 2356 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2357 |
+
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
| 2358 |
+
|
| 2359 |
+
[[package]]
|
| 2360 |
+
name = "want"
|
| 2361 |
+
version = "0.3.1"
|
| 2362 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2363 |
+
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
| 2364 |
+
dependencies = [
|
| 2365 |
+
"try-lock",
|
| 2366 |
+
]
|
| 2367 |
+
|
| 2368 |
+
[[package]]
|
| 2369 |
+
name = "wasi"
|
| 2370 |
+
version = "0.11.0+wasi-snapshot-preview1"
|
| 2371 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2372 |
+
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
| 2373 |
+
|
| 2374 |
+
[[package]]
|
| 2375 |
+
name = "wasm-bindgen"
|
| 2376 |
+
version = "0.2.100"
|
| 2377 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2378 |
+
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
| 2379 |
+
dependencies = [
|
| 2380 |
+
"cfg-if",
|
| 2381 |
+
"once_cell",
|
| 2382 |
+
"rustversion",
|
| 2383 |
+
"wasm-bindgen-macro",
|
| 2384 |
+
]
|
| 2385 |
+
|
| 2386 |
+
[[package]]
|
| 2387 |
+
name = "wasm-bindgen-backend"
|
| 2388 |
+
version = "0.2.100"
|
| 2389 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2390 |
+
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
| 2391 |
+
dependencies = [
|
| 2392 |
+
"bumpalo",
|
| 2393 |
+
"log",
|
| 2394 |
+
"proc-macro2",
|
| 2395 |
+
"quote",
|
| 2396 |
+
"syn 2.0.96",
|
| 2397 |
+
"wasm-bindgen-shared",
|
| 2398 |
+
]
|
| 2399 |
+
|
| 2400 |
+
[[package]]
|
| 2401 |
+
name = "wasm-bindgen-futures"
|
| 2402 |
+
version = "0.4.50"
|
| 2403 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2404 |
+
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
| 2405 |
+
dependencies = [
|
| 2406 |
+
"cfg-if",
|
| 2407 |
+
"js-sys",
|
| 2408 |
+
"once_cell",
|
| 2409 |
+
"wasm-bindgen",
|
| 2410 |
+
"web-sys",
|
| 2411 |
+
]
|
| 2412 |
+
|
| 2413 |
+
[[package]]
|
| 2414 |
+
name = "wasm-bindgen-macro"
|
| 2415 |
+
version = "0.2.100"
|
| 2416 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2417 |
+
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
| 2418 |
+
dependencies = [
|
| 2419 |
+
"quote",
|
| 2420 |
+
"wasm-bindgen-macro-support",
|
| 2421 |
+
]
|
| 2422 |
+
|
| 2423 |
+
[[package]]
|
| 2424 |
+
name = "wasm-bindgen-macro-support"
|
| 2425 |
+
version = "0.2.100"
|
| 2426 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2427 |
+
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
| 2428 |
+
dependencies = [
|
| 2429 |
+
"proc-macro2",
|
| 2430 |
+
"quote",
|
| 2431 |
+
"syn 2.0.96",
|
| 2432 |
+
"wasm-bindgen-backend",
|
| 2433 |
+
"wasm-bindgen-shared",
|
| 2434 |
+
]
|
| 2435 |
+
|
| 2436 |
+
[[package]]
|
| 2437 |
+
name = "wasm-bindgen-shared"
|
| 2438 |
+
version = "0.2.100"
|
| 2439 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2440 |
+
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
| 2441 |
+
dependencies = [
|
| 2442 |
+
"unicode-ident",
|
| 2443 |
+
]
|
| 2444 |
+
|
| 2445 |
+
[[package]]
|
| 2446 |
+
name = "wasm-streams"
|
| 2447 |
+
version = "0.4.2"
|
| 2448 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2449 |
+
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
| 2450 |
+
dependencies = [
|
| 2451 |
+
"futures-util",
|
| 2452 |
+
"js-sys",
|
| 2453 |
+
"wasm-bindgen",
|
| 2454 |
+
"wasm-bindgen-futures",
|
| 2455 |
+
"web-sys",
|
| 2456 |
+
]
|
| 2457 |
+
|
| 2458 |
+
[[package]]
|
| 2459 |
+
name = "web-sys"
|
| 2460 |
+
version = "0.3.77"
|
| 2461 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2462 |
+
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
| 2463 |
+
dependencies = [
|
| 2464 |
+
"js-sys",
|
| 2465 |
+
"wasm-bindgen",
|
| 2466 |
+
]
|
| 2467 |
+
|
| 2468 |
+
[[package]]
|
| 2469 |
+
name = "weezl"
|
| 2470 |
+
version = "0.1.8"
|
| 2471 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2472 |
+
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
| 2473 |
+
|
| 2474 |
+
[[package]]
|
| 2475 |
+
name = "winapi"
|
| 2476 |
+
version = "0.3.9"
|
| 2477 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2478 |
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
| 2479 |
+
dependencies = [
|
| 2480 |
+
"winapi-i686-pc-windows-gnu",
|
| 2481 |
+
"winapi-x86_64-pc-windows-gnu",
|
| 2482 |
+
]
|
| 2483 |
+
|
| 2484 |
+
[[package]]
|
| 2485 |
+
name = "winapi-i686-pc-windows-gnu"
|
| 2486 |
+
version = "0.4.0"
|
| 2487 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2488 |
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
| 2489 |
+
|
| 2490 |
+
[[package]]
|
| 2491 |
+
name = "winapi-x86_64-pc-windows-gnu"
|
| 2492 |
+
version = "0.4.0"
|
| 2493 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2494 |
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
| 2495 |
+
|
| 2496 |
+
[[package]]
|
| 2497 |
+
name = "windows"
|
| 2498 |
+
version = "0.57.0"
|
| 2499 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2500 |
+
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
| 2501 |
+
dependencies = [
|
| 2502 |
+
"windows-core 0.57.0",
|
| 2503 |
+
"windows-targets",
|
| 2504 |
+
]
|
| 2505 |
+
|
| 2506 |
+
[[package]]
|
| 2507 |
+
name = "windows-core"
|
| 2508 |
+
version = "0.52.0"
|
| 2509 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2510 |
+
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
| 2511 |
+
dependencies = [
|
| 2512 |
+
"windows-targets",
|
| 2513 |
+
]
|
| 2514 |
+
|
| 2515 |
+
[[package]]
|
| 2516 |
+
name = "windows-core"
|
| 2517 |
+
version = "0.57.0"
|
| 2518 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2519 |
+
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
| 2520 |
+
dependencies = [
|
| 2521 |
+
"windows-implement",
|
| 2522 |
+
"windows-interface",
|
| 2523 |
+
"windows-result 0.1.2",
|
| 2524 |
+
"windows-targets",
|
| 2525 |
+
]
|
| 2526 |
+
|
| 2527 |
+
[[package]]
|
| 2528 |
+
name = "windows-implement"
|
| 2529 |
+
version = "0.57.0"
|
| 2530 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2531 |
+
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
| 2532 |
+
dependencies = [
|
| 2533 |
+
"proc-macro2",
|
| 2534 |
+
"quote",
|
| 2535 |
+
"syn 2.0.96",
|
| 2536 |
+
]
|
| 2537 |
+
|
| 2538 |
+
[[package]]
|
| 2539 |
+
name = "windows-interface"
|
| 2540 |
+
version = "0.57.0"
|
| 2541 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2542 |
+
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
| 2543 |
+
dependencies = [
|
| 2544 |
+
"proc-macro2",
|
| 2545 |
+
"quote",
|
| 2546 |
+
"syn 2.0.96",
|
| 2547 |
+
]
|
| 2548 |
+
|
| 2549 |
+
[[package]]
|
| 2550 |
+
name = "windows-registry"
|
| 2551 |
+
version = "0.2.0"
|
| 2552 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2553 |
+
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
| 2554 |
+
dependencies = [
|
| 2555 |
+
"windows-result 0.2.0",
|
| 2556 |
+
"windows-strings",
|
| 2557 |
+
"windows-targets",
|
| 2558 |
+
]
|
| 2559 |
+
|
| 2560 |
+
[[package]]
|
| 2561 |
+
name = "windows-result"
|
| 2562 |
+
version = "0.1.2"
|
| 2563 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2564 |
+
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
| 2565 |
+
dependencies = [
|
| 2566 |
+
"windows-targets",
|
| 2567 |
+
]
|
| 2568 |
+
|
| 2569 |
+
[[package]]
|
| 2570 |
+
name = "windows-result"
|
| 2571 |
+
version = "0.2.0"
|
| 2572 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2573 |
+
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
| 2574 |
+
dependencies = [
|
| 2575 |
+
"windows-targets",
|
| 2576 |
+
]
|
| 2577 |
+
|
| 2578 |
+
[[package]]
|
| 2579 |
+
name = "windows-strings"
|
| 2580 |
+
version = "0.1.0"
|
| 2581 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2582 |
+
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
| 2583 |
+
dependencies = [
|
| 2584 |
+
"windows-result 0.2.0",
|
| 2585 |
+
"windows-targets",
|
| 2586 |
+
]
|
| 2587 |
+
|
| 2588 |
+
[[package]]
|
| 2589 |
+
name = "windows-sys"
|
| 2590 |
+
version = "0.52.0"
|
| 2591 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2592 |
+
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
| 2593 |
+
dependencies = [
|
| 2594 |
+
"windows-targets",
|
| 2595 |
+
]
|
| 2596 |
+
|
| 2597 |
+
[[package]]
|
| 2598 |
+
name = "windows-sys"
|
| 2599 |
+
version = "0.59.0"
|
| 2600 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2601 |
+
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
| 2602 |
+
dependencies = [
|
| 2603 |
+
"windows-targets",
|
| 2604 |
+
]
|
| 2605 |
+
|
| 2606 |
+
[[package]]
|
| 2607 |
+
name = "windows-targets"
|
| 2608 |
+
version = "0.52.6"
|
| 2609 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2610 |
+
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
| 2611 |
+
dependencies = [
|
| 2612 |
+
"windows_aarch64_gnullvm",
|
| 2613 |
+
"windows_aarch64_msvc",
|
| 2614 |
+
"windows_i686_gnu",
|
| 2615 |
+
"windows_i686_gnullvm",
|
| 2616 |
+
"windows_i686_msvc",
|
| 2617 |
+
"windows_x86_64_gnu",
|
| 2618 |
+
"windows_x86_64_gnullvm",
|
| 2619 |
+
"windows_x86_64_msvc",
|
| 2620 |
+
]
|
| 2621 |
+
|
| 2622 |
+
[[package]]
|
| 2623 |
+
name = "windows_aarch64_gnullvm"
|
| 2624 |
+
version = "0.52.6"
|
| 2625 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2626 |
+
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
| 2627 |
+
|
| 2628 |
+
[[package]]
|
| 2629 |
+
name = "windows_aarch64_msvc"
|
| 2630 |
+
version = "0.52.6"
|
| 2631 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2632 |
+
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
| 2633 |
+
|
| 2634 |
+
[[package]]
|
| 2635 |
+
name = "windows_i686_gnu"
|
| 2636 |
+
version = "0.52.6"
|
| 2637 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2638 |
+
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
| 2639 |
+
|
| 2640 |
+
[[package]]
|
| 2641 |
+
name = "windows_i686_gnullvm"
|
| 2642 |
+
version = "0.52.6"
|
| 2643 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2644 |
+
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
| 2645 |
+
|
| 2646 |
+
[[package]]
|
| 2647 |
+
name = "windows_i686_msvc"
|
| 2648 |
+
version = "0.52.6"
|
| 2649 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2650 |
+
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
| 2651 |
+
|
| 2652 |
+
[[package]]
|
| 2653 |
+
name = "windows_x86_64_gnu"
|
| 2654 |
+
version = "0.52.6"
|
| 2655 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2656 |
+
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
| 2657 |
+
|
| 2658 |
+
[[package]]
|
| 2659 |
+
name = "windows_x86_64_gnullvm"
|
| 2660 |
+
version = "0.52.6"
|
| 2661 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2662 |
+
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
| 2663 |
+
|
| 2664 |
+
[[package]]
|
| 2665 |
+
name = "windows_x86_64_msvc"
|
| 2666 |
+
version = "0.52.6"
|
| 2667 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2668 |
+
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
| 2669 |
+
|
| 2670 |
+
[[package]]
|
| 2671 |
+
name = "write16"
|
| 2672 |
+
version = "1.0.0"
|
| 2673 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2674 |
+
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
| 2675 |
+
|
| 2676 |
+
[[package]]
|
| 2677 |
+
name = "writeable"
|
| 2678 |
+
version = "0.5.5"
|
| 2679 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2680 |
+
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
| 2681 |
+
|
| 2682 |
+
[[package]]
|
| 2683 |
+
name = "wyz"
|
| 2684 |
+
version = "0.5.1"
|
| 2685 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2686 |
+
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
| 2687 |
+
dependencies = [
|
| 2688 |
+
"tap",
|
| 2689 |
+
]
|
| 2690 |
+
|
| 2691 |
+
[[package]]
|
| 2692 |
+
name = "yoke"
|
| 2693 |
+
version = "0.7.5"
|
| 2694 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2695 |
+
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
| 2696 |
+
dependencies = [
|
| 2697 |
+
"serde",
|
| 2698 |
+
"stable_deref_trait",
|
| 2699 |
+
"yoke-derive",
|
| 2700 |
+
"zerofrom",
|
| 2701 |
+
]
|
| 2702 |
+
|
| 2703 |
+
[[package]]
|
| 2704 |
+
name = "yoke-derive"
|
| 2705 |
+
version = "0.7.5"
|
| 2706 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2707 |
+
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
| 2708 |
+
dependencies = [
|
| 2709 |
+
"proc-macro2",
|
| 2710 |
+
"quote",
|
| 2711 |
+
"syn 2.0.96",
|
| 2712 |
+
"synstructure",
|
| 2713 |
+
]
|
| 2714 |
+
|
| 2715 |
+
[[package]]
|
| 2716 |
+
name = "zerocopy"
|
| 2717 |
+
version = "0.7.35"
|
| 2718 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2719 |
+
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
| 2720 |
+
dependencies = [
|
| 2721 |
+
"byteorder",
|
| 2722 |
+
"zerocopy-derive",
|
| 2723 |
+
]
|
| 2724 |
+
|
| 2725 |
+
[[package]]
|
| 2726 |
+
name = "zerocopy-derive"
|
| 2727 |
+
version = "0.7.35"
|
| 2728 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2729 |
+
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
| 2730 |
+
dependencies = [
|
| 2731 |
+
"proc-macro2",
|
| 2732 |
+
"quote",
|
| 2733 |
+
"syn 2.0.96",
|
| 2734 |
+
]
|
| 2735 |
+
|
| 2736 |
+
[[package]]
|
| 2737 |
+
name = "zerofrom"
|
| 2738 |
+
version = "0.1.5"
|
| 2739 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2740 |
+
checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
|
| 2741 |
+
dependencies = [
|
| 2742 |
+
"zerofrom-derive",
|
| 2743 |
+
]
|
| 2744 |
+
|
| 2745 |
+
[[package]]
|
| 2746 |
+
name = "zerofrom-derive"
|
| 2747 |
+
version = "0.1.5"
|
| 2748 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2749 |
+
checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
| 2750 |
+
dependencies = [
|
| 2751 |
+
"proc-macro2",
|
| 2752 |
+
"quote",
|
| 2753 |
+
"syn 2.0.96",
|
| 2754 |
+
"synstructure",
|
| 2755 |
+
]
|
| 2756 |
+
|
| 2757 |
+
[[package]]
|
| 2758 |
+
name = "zeroize"
|
| 2759 |
+
version = "1.8.1"
|
| 2760 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2761 |
+
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
| 2762 |
+
|
| 2763 |
+
[[package]]
|
| 2764 |
+
name = "zerovec"
|
| 2765 |
+
version = "0.10.4"
|
| 2766 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2767 |
+
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
| 2768 |
+
dependencies = [
|
| 2769 |
+
"yoke",
|
| 2770 |
+
"zerofrom",
|
| 2771 |
+
"zerovec-derive",
|
| 2772 |
+
]
|
| 2773 |
+
|
| 2774 |
+
[[package]]
|
| 2775 |
+
name = "zerovec-derive"
|
| 2776 |
+
version = "0.10.3"
|
| 2777 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2778 |
+
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
| 2779 |
+
dependencies = [
|
| 2780 |
+
"proc-macro2",
|
| 2781 |
+
"quote",
|
| 2782 |
+
"syn 2.0.96",
|
| 2783 |
+
]
|
| 2784 |
+
|
| 2785 |
+
[[package]]
|
| 2786 |
+
name = "zune-core"
|
| 2787 |
+
version = "0.4.12"
|
| 2788 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2789 |
+
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
| 2790 |
+
|
| 2791 |
+
[[package]]
|
| 2792 |
+
name = "zune-jpeg"
|
| 2793 |
+
version = "0.4.14"
|
| 2794 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 2795 |
+
checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
|
| 2796 |
+
dependencies = [
|
| 2797 |
+
"zune-core",
|
| 2798 |
+
]
|
Cargo.toml
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[package]
|
| 2 |
+
name = "cursor-api"
|
| 3 |
+
version = "0.1.3-rc.4.3"
|
| 4 |
+
edition = "2021"
|
| 5 |
+
authors = ["wisdgod <nav@wisdgod.com>"]
|
| 6 |
+
description = "OpenAI format compatibility layer for the Cursor API"
|
| 7 |
+
repository = "https://github.com/wisdgod/cursor-api"
|
| 8 |
+
|
| 9 |
+
[build-dependencies]
|
| 10 |
+
prost-build = "0.13.4"
|
| 11 |
+
sha2 = { version = "0.10.8", default-features = false }
|
| 12 |
+
serde_json = "1.0.134"
|
| 13 |
+
|
| 14 |
+
[dependencies]
|
| 15 |
+
axum = { version = "0.8.1", features = ["json"] }
|
| 16 |
+
base64 = { version = "0.22.1", default-features = false, features = ["std"] }
|
| 17 |
+
# brotli = { version = "7.0.0", default-features = false, features = ["std"] }
|
| 18 |
+
bytes = "1.9.0"
|
| 19 |
+
chrono = { version = "0.4.39", default-features = false, features = ["std", "clock", "now", "serde", "rkyv-64"] }
|
| 20 |
+
dotenvy = "0.15.7"
|
| 21 |
+
flate2 = { version = "1.0.35", default-features = false, features = ["rust_backend"] }
|
| 22 |
+
futures = { version = "0.3.31", default-features = false, features = ["std"] }
|
| 23 |
+
gif = { version = "0.13.1", default-features = false, features = ["std"] }
|
| 24 |
+
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
| 25 |
+
image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "gif", "webp"] }
|
| 26 |
+
memmap2 = "0.9.5"
|
| 27 |
+
# openssl = { version = "0.10.68", features = ["vendored"] }
|
| 28 |
+
parking_lot = "0.12.3"
|
| 29 |
+
paste = "1.0.15"
|
| 30 |
+
prost = "0.13.4"
|
| 31 |
+
rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] }
|
| 32 |
+
regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] }
|
| 33 |
+
reqwest = { version = "0.12.12", default-features = false, features = ["gzip", "brotli", "json", "stream", "socks", "__tls", "charset", "default-tls", "h2", "http2", "macos-system-configuration"] }
|
| 34 |
+
rkyv = { version = "0.7.45", default-features = false, features = ["alloc", "std", "bytecheck", "size_64", "validation", "std"] }
|
| 35 |
+
serde = { version = "1.0.217", default-features = false, features = ["std", "derive"] }
|
| 36 |
+
serde_json = { package = "sonic-rs", version = "0.3.17" }
|
| 37 |
+
# serde_json = "1.0.137"
|
| 38 |
+
sha2 = { version = "0.10.8", default-features = false }
|
| 39 |
+
sysinfo = { version = "0.33.1", default-features = false, features = ["system"] }
|
| 40 |
+
tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros", "net", "sync", "time", "fs", "signal"] }
|
| 41 |
+
tokio-stream = { version = "0.1.17", features = ["time"] }
|
| 42 |
+
tower-http = { version = "0.6.2", features = ["cors", "limit"] }
|
| 43 |
+
url = { version = "2.5.4", default-features = false }
|
| 44 |
+
uuid = { version = "1.12.1", features = ["v4"] }
|
| 45 |
+
|
| 46 |
+
[profile.release]
|
| 47 |
+
lto = true
|
| 48 |
+
codegen-units = 1
|
| 49 |
+
panic = 'abort'
|
| 50 |
+
strip = true
|
| 51 |
+
opt-level = 3
|
| 52 |
+
|
| 53 |
+
[features]
|
| 54 |
+
default = []
|
| 55 |
+
use-minified = []
|
Cross.toml
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[target.x86_64-unknown-linux-gnu]
|
| 2 |
+
dockerfile = "Dockerfile.cross"
|
| 3 |
+
|
| 4 |
+
[target.aarch64-unknown-linux-gnu]
|
| 5 |
+
dockerfile = "Dockerfile.cross.arm64"
|
Cursor API.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Cursor API
|
| 2 |
+
|
| 3 |
+
## 项目说明
|
| 4 |
+
|
| 5 |
+
### 版本声明
|
| 6 |
+
- 当前版本已进入稳定阶段
|
| 7 |
+
- 以下问题与程序无关,请勿反馈:
|
| 8 |
+
- 响应缺字漏字
|
| 9 |
+
- 首字延迟现象
|
| 10 |
+
- 响应出现乱码
|
| 11 |
+
- 性能优势:
|
| 12 |
+
- 达到原生客户端响应速度
|
| 13 |
+
- 部分场景下表现更优
|
| 14 |
+
- 开源协议要求:
|
| 15 |
+
- Fork 项目禁止以原作者名义进行宣传推广
|
| 16 |
+
- 禁止发布任何形式的官方声明
|
| 17 |
+
|
| 18 |
+

|
| 19 |
+
|
| 20 |
+
## 快速入门
|
| 21 |
+
|
| 22 |
+
### 密钥获取
|
| 23 |
+
1. 访问 [Cursor 官网](https://www.cursor.com) 完成注册登录
|
| 24 |
+
2. 开启浏览器开发者工具 (F12)
|
| 25 |
+
3. 在 Application → Cookies 中定位 `WorkosCursorSessionToken`
|
| 26 |
+
4. 复制第三个字段值(注意:`%3A%3A` 为 `::` 的 URL 编码形式)
|
| 27 |
+
|
| 28 |
+
## 配置指南
|
| 29 |
+
|
| 30 |
+
### 环境变量
|
| 31 |
+
| 变量名 | 类型 | 默认值 | 说明 |
|
| 32 |
+
|--------|------|--------|-----|
|
| 33 |
+
| PORT | int | 3000 | 服务端口号 |
|
| 34 |
+
| AUTH_TOKEN | string | 无 | 认证令牌(必需) |
|
| 35 |
+
| ROUTE_PREFIX | string | 无 | 路由前缀 |
|
| 36 |
+
| TOKEN_LIST_FILE | string | .tokens | Token 存储文件 |
|
| 37 |
+
|
| 38 |
+
完整配置参见 [env-example](/env-example)
|
| 39 |
+
|
| 40 |
+
### Token 文件规范
|
| 41 |
+
`.tokens` 文件格式:
|
| 42 |
+
```plaintext
|
| 43 |
+
# 注释行将在下次读取时自动删除
|
| 44 |
+
token1,checksum1
|
| 45 |
+
token2,checksum2
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
文件管理原则:
|
| 49 |
+
- 系统自动维护文件内容
|
| 50 |
+
- 仅以下情况需要手动编辑:
|
| 51 |
+
- 删除特定 token
|
| 52 |
+
- 绑定已有 checksum 到指定 token
|
| 53 |
+
|
| 54 |
+
## 模型支持列表
|
| 55 |
+
```json
|
| 56 |
+
[
|
| 57 |
+
"claude-3.5-sonnet",
|
| 58 |
+
"gpt-4",
|
| 59 |
+
"gpt-4o",
|
| 60 |
+
"cursor-fast",
|
| 61 |
+
"gpt-4o-mini",
|
| 62 |
+
"deepseek-v3"
|
| 63 |
+
]
|
| 64 |
+
```
|
| 65 |
+
*注:模型列表为固定配置,暂不支持自定义扩展*
|
| 66 |
+
|
| 67 |
+
## API 文档
|
| 68 |
+
|
| 69 |
+
### 基础对话接口
|
| 70 |
+
**Endpoint**
|
| 71 |
+
`POST /v1/chat/completions`
|
| 72 |
+
|
| 73 |
+
**认证方式**
|
| 74 |
+
`Bearer Token` 三级认证机制:
|
| 75 |
+
1. 环境变量 `AUTH_TOKEN`
|
| 76 |
+
2. `.token` 文件轮询
|
| 77 |
+
3. 直接 token,checksum 认证(v0.1.3-rc.3+)
|
| 78 |
+
|
| 79 |
+
**请求示例**
|
| 80 |
+
```json
|
| 81 |
+
{
|
| 82 |
+
"model": "gpt-4",
|
| 83 |
+
"messages": [
|
| 84 |
+
{
|
| 85 |
+
"role": "user",
|
| 86 |
+
"content": "解释量子计算的基本原理"
|
| 87 |
+
}
|
| 88 |
+
],
|
| 89 |
+
"stream": false
|
| 90 |
+
}
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
**响应示例(非流式)**
|
| 94 |
+
```json
|
| 95 |
+
{
|
| 96 |
+
"id": "chatcmpl-9Xy...",
|
| 97 |
+
"object": "chat.completion",
|
| 98 |
+
"created": 1628063500,
|
| 99 |
+
"model": "gpt-4",
|
| 100 |
+
"choices": [{
|
| 101 |
+
"index": 0,
|
| 102 |
+
"message": {
|
| 103 |
+
"role": "assistant",
|
| 104 |
+
"content": "量子计算基于量子比特..."
|
| 105 |
+
},
|
| 106 |
+
"finish_reason": "stop"
|
| 107 |
+
}]
|
| 108 |
+
}
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
### Token 管理接口
|
| 112 |
+
| 端点 | 方法 | 功能 |
|
| 113 |
+
|------|------|-----|
|
| 114 |
+
| `/tokens` | GET | Token 信息管理界面 |
|
| 115 |
+
| `/tokens/update` | POST | 批量更新 Token 列表 |
|
| 116 |
+
| `/tokens/add` | POST | 增量添加 Token |
|
| 117 |
+
| `/tokens/delete` | POST | 删除指定 Token |
|
| 118 |
+
|
| 119 |
+
```mermaid
|
| 120 |
+
sequenceDiagram
|
| 121 |
+
participant Client
|
| 122 |
+
participant API
|
| 123 |
+
Client->>API: POST /tokens/add
|
| 124 |
+
API->>API: 验证Token有效性
|
| 125 |
+
API->>File: 写入.tokens
|
| 126 |
+
API-->>Client: 返回更新结果
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
## 高级功能
|
| 130 |
+
|
| 131 |
+
### 动态密钥生成
|
| 132 |
+
**Endpoint**
|
| 133 |
+
`POST /build-key`
|
| 134 |
+
|
| 135 |
+
**优势对比**
|
| 136 |
+
| 特性 | 传统模式 | 动态密钥 |
|
| 137 |
+
|------|---------|---------|
|
| 138 |
+
| 密钥长度 | 较长 | 优化缩短 |
|
| 139 |
+
| 配置扩展 | 无 | 支持自定义 |
|
| 140 |
+
| 安全等级 | 基础 | 增强编码 |
|
| 141 |
+
| 验证效率 | 预校验耗时 | 即时验证 |
|
| 142 |
+
|
| 143 |
+
## 系统监控
|
| 144 |
+
|
| 145 |
+
### 健康检查
|
| 146 |
+
**Endpoint**
|
| 147 |
+
`GET /health`
|
| 148 |
+
|
| 149 |
+
**响应示例**
|
| 150 |
+
```json
|
| 151 |
+
{
|
| 152 |
+
"status": "success",
|
| 153 |
+
"version": "1.2.0",
|
| 154 |
+
"uptime": 86400,
|
| 155 |
+
"models": ["gpt-4", "claude-3.5"],
|
| 156 |
+
"endpoints": ["/v1/chat", "/tokens"]
|
| 157 |
+
}
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
## 生态工具
|
| 161 |
+
|
| 162 |
+
### 开发辅助工具
|
| 163 |
+
- [Token 获取工具](https://github.com/wisdgod/cursor-api/tree/main/tools/get-token)
|
| 164 |
+
支持 Windows/Linux/macOS 系统
|
| 165 |
+
- [遥测数据重置工具](https://github.com/wisdgod/cursor-api/tree/main/tools/reset-telemetry)
|
| 166 |
+
清除用户使用数据记录
|
| 167 |
+
|
| 168 |
+
## 致谢声明
|
| 169 |
+
本项目的发展离不开以下开源项目的启发:
|
| 170 |
+
- [zhx47/cursor-api](https://github.com/zhx47/cursor-api) - 基础架构参考
|
| 171 |
+
- [cursorToApi](https://github.com/luolazyandlazy/cursorToApi) - 认证机制优化方案
|
| 172 |
+
|
| 173 |
+
---
|
| 174 |
+
|
| 175 |
+
> **项目维护说明**
|
| 176 |
+
> 我们欢迎社区贡献,但请注意:
|
| 177 |
+
> 1. 功能请求需附带使用场景说明
|
| 178 |
+
> 2. Bug 报告请提供复现步骤和环境信息
|
| 179 |
+
> 3. 重要变更需通过 CI/CD 测试流程
|
Dockerfile
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ARG TARGETARCH
|
| 2 |
+
FROM --platform=linux/${TARGETARCH} rust:1.84.0-slim-bookworm as builder
|
| 3 |
+
|
| 4 |
+
ARG TARGETARCH
|
| 5 |
+
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
RUN apt-get update && \
|
| 8 |
+
apt-get install -y --no-install-recommends \
|
| 9 |
+
build-essential protobuf-compiler pkg-config libssl-dev nodejs npm openssl \
|
| 10 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 11 |
+
|
| 12 |
+
COPY . .
|
| 13 |
+
RUN case "$TARGETARCH" in \
|
| 14 |
+
amd64) TARGET_CPU="x86-64-v3" ;; \
|
| 15 |
+
arm64) TARGET_CPU="neoverse-n1" ;; \
|
| 16 |
+
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
|
| 17 |
+
esac && \
|
| 18 |
+
RUSTFLAGS="-C link-arg=-s -C target-cpu=$TARGET_CPU" cargo build --release && \
|
| 19 |
+
cp target/release/cursor-api /app/cursor-api
|
| 20 |
+
|
| 21 |
+
# 运行阶段
|
| 22 |
+
FROM --platform=linux/${TARGETARCH} debian:bookworm-slim
|
| 23 |
+
|
| 24 |
+
WORKDIR /app
|
| 25 |
+
ENV TZ=Asia/Shanghai
|
| 26 |
+
|
| 27 |
+
RUN apt-get update && \
|
| 28 |
+
apt-get install -y --no-install-recommends \
|
| 29 |
+
ca-certificates tzdata openssl \
|
| 30 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 31 |
+
|
| 32 |
+
COPY --from=builder /app/cursor-api .
|
| 33 |
+
|
| 34 |
+
ENV PORT=3000
|
| 35 |
+
EXPOSE ${PORT}
|
| 36 |
+
CMD ["./cursor-api"]
|
Dockerfile.cross
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dockerfile.cross
|
| 2 |
+
|
| 3 |
+
FROM --platform=linux/amd64 rust:1.84.0-slim-bookworm
|
| 4 |
+
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# 安装必要的软件包
|
| 8 |
+
RUN apt-get update && \
|
| 9 |
+
apt-get install -y --no-install-recommends \
|
| 10 |
+
build-essential \
|
| 11 |
+
pkg-config \
|
| 12 |
+
libssl-dev \
|
| 13 |
+
protobuf-compiler \
|
| 14 |
+
openssl \
|
| 15 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 16 |
+
|
| 17 |
+
# 设置环境变量 (如果需要)
|
| 18 |
+
# ENV RUSTFLAGS="-C link-arg=-s"
|
| 19 |
+
|
| 20 |
+
# 设置 PROTOC 环境变量 (因为你的 build.rs 需要)
|
| 21 |
+
ENV PROTOC=/usr/bin/protoc
|
| 22 |
+
|
| 23 |
+
# 安装特定版本的 protoc (如果你需要特定版本,例如 29.3;否则可以删除这部分)
|
| 24 |
+
# ENV PROTOC_VERSION=29.3
|
| 25 |
+
# ENV PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-x86_64.zip
|
| 26 |
+
# RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP} -O /tmp/${PROTOC_ZIP} && \
|
| 27 |
+
# unzip /tmp/${PROTOC_ZIP} -d /usr && \
|
| 28 |
+
# rm /tmp/${PROTOC_ZIP}
|
| 29 |
+
|
| 30 |
+
# 验证安装
|
| 31 |
+
RUN protoc --version
|
Dockerfile.cross.arm64
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dockerfile.cross
|
| 2 |
+
|
| 3 |
+
FROM --platform=linux/arm64 rust:1.84.0-slim-bookworm
|
| 4 |
+
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# 安装必要的软件包
|
| 8 |
+
RUN apt-get update && \
|
| 9 |
+
apt-get install -y --no-install-recommends \
|
| 10 |
+
build-essential \
|
| 11 |
+
pkg-config \
|
| 12 |
+
libssl-dev \
|
| 13 |
+
protobuf-compiler \
|
| 14 |
+
openssl \
|
| 15 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 16 |
+
|
| 17 |
+
# 设置环境变量 (如果需要)
|
| 18 |
+
# ENV RUSTFLAGS="-C link-arg=-s"
|
| 19 |
+
|
| 20 |
+
# 设置 PROTOC 环境变量 (因为你的 build.rs 需要)
|
| 21 |
+
ENV PROTOC=/usr/bin/protoc
|
| 22 |
+
|
| 23 |
+
# 安装特定版本的 protoc (如果你需要特定版本,例如 29.3;否则可以删除这部分)
|
| 24 |
+
# ENV PROTOC_VERSION=29.3
|
| 25 |
+
# ENV PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-x86_64.zip
|
| 26 |
+
# RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP} -O /tmp/${PROTOC_ZIP} && \
|
| 27 |
+
# unzip /tmp/${PROTOC_ZIP} -d /usr && \
|
| 28 |
+
# rm /tmp/${PROTOC_ZIP}
|
| 29 |
+
|
| 30 |
+
# 验证安装
|
| 31 |
+
RUN protoc --version
|
README.md
CHANGED
|
@@ -5,6 +5,7 @@ colorFrom: purple
|
|
| 5 |
colorTo: gray
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 5 |
colorTo: gray
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
app_port: 3000
|
| 9 |
---
|
| 10 |
|
| 11 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
scripts/build.ps1
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 参数处理
|
| 2 |
+
param(
|
| 3 |
+
[switch]$Static,
|
| 4 |
+
[switch]$Help,
|
| 5 |
+
[ValidateSet("x86_64", "aarch64", "i686")]
|
| 6 |
+
[string]$Architecture
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
# 设置错误时停止执行
|
| 10 |
+
$ErrorActionPreference = "Stop"
|
| 11 |
+
|
| 12 |
+
# 颜色输出函数
|
| 13 |
+
function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
|
| 14 |
+
function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow }
|
| 15 |
+
function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 }
|
| 16 |
+
|
| 17 |
+
# 检查必要的工具
|
| 18 |
+
function Check-Requirements {
|
| 19 |
+
$tools = @("cargo", "protoc", "npm", "node")
|
| 20 |
+
$missing = @()
|
| 21 |
+
|
| 22 |
+
foreach ($tool in $tools) {
|
| 23 |
+
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
|
| 24 |
+
$missing += $tool
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
if ($missing.Count -gt 0) {
|
| 29 |
+
Write-Error "缺少必要工具: $($missing -join ', ')"
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
# 帮助信息
|
| 34 |
+
function Show-Help {
|
| 35 |
+
Write-Host @"
|
| 36 |
+
用法: $(Split-Path $MyInvocation.ScriptName -Leaf) [选项]
|
| 37 |
+
|
| 38 |
+
选项:
|
| 39 |
+
-Static 使用静态链接(默认动态链接)
|
| 40 |
+
-Help 显示此帮助信息
|
| 41 |
+
|
| 42 |
+
不带参数时使用默认配置构建
|
| 43 |
+
"@
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
# 构建函数
|
| 47 |
+
function Build-Target {
|
| 48 |
+
param (
|
| 49 |
+
[string]$Target,
|
| 50 |
+
[string]$RustFlags
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
Write-Info "正在构建 $Target..."
|
| 54 |
+
|
| 55 |
+
# 设置环境变量
|
| 56 |
+
$env:RUSTFLAGS = $RustFlags
|
| 57 |
+
|
| 58 |
+
# 构建
|
| 59 |
+
if ($Target -ne (rustc -Vv | Select-String "host: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value })) {
|
| 60 |
+
cargo build --target $Target --release
|
| 61 |
+
} else {
|
| 62 |
+
cargo build --release
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
# 移动编译产物到 release 目录
|
| 66 |
+
$binaryName = "cursor-api"
|
| 67 |
+
if ($Static) {
|
| 68 |
+
$binaryName += "-static"
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
$binaryPath = if ($Target -eq (rustc -Vv | Select-String "host: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value })) {
|
| 72 |
+
"target/release/cursor-api.exe"
|
| 73 |
+
} else {
|
| 74 |
+
"target/$Target/release/cursor-api.exe"
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
if (Test-Path $binaryPath) {
|
| 78 |
+
Copy-Item $binaryPath "release/$binaryName-$Target.exe"
|
| 79 |
+
Write-Info "完成构建 $Target"
|
| 80 |
+
} else {
|
| 81 |
+
Write-Warn "构建产物未找到: $Target"
|
| 82 |
+
Write-Warn "查找路径: $binaryPath"
|
| 83 |
+
Write-Warn "当前目录内容:"
|
| 84 |
+
Get-ChildItem -Recurse target/
|
| 85 |
+
return $false
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
return $true
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
if ($Help) {
|
| 92 |
+
Show-Help
|
| 93 |
+
exit 0
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
# 检查依赖
|
| 97 |
+
Check-Requirements
|
| 98 |
+
|
| 99 |
+
# 创建 release 目录
|
| 100 |
+
New-Item -ItemType Directory -Force -Path release | Out-Null
|
| 101 |
+
|
| 102 |
+
# 设置静态链接标志
|
| 103 |
+
$rustFlags = ""
|
| 104 |
+
if ($Static) {
|
| 105 |
+
$rustFlags = "-C target-feature=+crt-static"
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
# 获取目标架构
|
| 109 |
+
$arch = if ($Architecture) {
|
| 110 |
+
$Architecture
|
| 111 |
+
} else {
|
| 112 |
+
switch ($env:PROCESSOR_ARCHITECTURE) {
|
| 113 |
+
"AMD64" { "x86_64" }
|
| 114 |
+
"ARM64" { "aarch64" }
|
| 115 |
+
"X86" { "i686" }
|
| 116 |
+
default { Write-Error "不支持的架构: $env:PROCESSOR_ARCHITECTURE" }
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
$target = "$arch-pc-windows-msvc"
|
| 120 |
+
|
| 121 |
+
Write-Info "开始构建..."
|
| 122 |
+
if (-not (Build-Target -Target $target -RustFlags $rustFlags)) {
|
| 123 |
+
Write-Error "构建失败"
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
Write-Info "构建完成!"
|
scripts/build.sh
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
# 颜色输出函数
|
| 5 |
+
info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
| 6 |
+
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
| 7 |
+
error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; }
|
| 8 |
+
|
| 9 |
+
# 检查必要的工具
|
| 10 |
+
check_requirements() {
|
| 11 |
+
local missing_tools=()
|
| 12 |
+
|
| 13 |
+
# 基础工具检查
|
| 14 |
+
for tool in cargo protoc npm node; do
|
| 15 |
+
if ! command -v "$tool" &>/dev/null; then
|
| 16 |
+
missing_tools+=("$tool")
|
| 17 |
+
fi
|
| 18 |
+
done
|
| 19 |
+
|
| 20 |
+
if [[ ${#missing_tools[@]} -gt 0 ]]; then
|
| 21 |
+
error "缺少必要工具: ${missing_tools[*]}"
|
| 22 |
+
fi
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
# 解析参数
|
| 26 |
+
USE_STATIC=false
|
| 27 |
+
|
| 28 |
+
while [[ $# -gt 0 ]]; do
|
| 29 |
+
case $1 in
|
| 30 |
+
--static) USE_STATIC=true ;;
|
| 31 |
+
--help) show_help; exit 0 ;;
|
| 32 |
+
*) error "未知参数: $1" ;;
|
| 33 |
+
esac
|
| 34 |
+
shift
|
| 35 |
+
done
|
| 36 |
+
|
| 37 |
+
# 帮助信息
|
| 38 |
+
show_help() {
|
| 39 |
+
cat << EOF
|
| 40 |
+
用法: $(basename "$0") [选项]
|
| 41 |
+
|
| 42 |
+
选项:
|
| 43 |
+
--static 使用静态链接(默认动态链接)
|
| 44 |
+
--help 显示此帮助信息
|
| 45 |
+
|
| 46 |
+
不带参数时只编译当前平台
|
| 47 |
+
EOF
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
# 并行构建函数
|
| 51 |
+
build_target() {
|
| 52 |
+
local target=$1
|
| 53 |
+
local extension=""
|
| 54 |
+
local rustflags="${2:-}"
|
| 55 |
+
|
| 56 |
+
info "正在构建 $target..."
|
| 57 |
+
|
| 58 |
+
# 确定文件后缀
|
| 59 |
+
[[ $target == *"windows"* ]] && extension=".exe"
|
| 60 |
+
|
| 61 |
+
# 构建
|
| 62 |
+
if [[ $target != "$CURRENT_TARGET" ]]; then
|
| 63 |
+
env RUSTFLAGS="$rustflags" cargo build --target "$target" --release
|
| 64 |
+
else
|
| 65 |
+
env RUSTFLAGS="$rustflags" cargo build --release
|
| 66 |
+
fi
|
| 67 |
+
|
| 68 |
+
# 移动编译产物到 release 目录
|
| 69 |
+
local binary_name="cursor-api"
|
| 70 |
+
[[ $USE_STATIC == true ]] && binary_name+="-static"
|
| 71 |
+
|
| 72 |
+
local binary_path
|
| 73 |
+
if [[ $target == "$CURRENT_TARGET" ]]; then
|
| 74 |
+
binary_path="target/release/cursor-api$extension"
|
| 75 |
+
else
|
| 76 |
+
binary_path="target/$target/release/cursor-api$extension"
|
| 77 |
+
fi
|
| 78 |
+
|
| 79 |
+
if [[ -f "$binary_path" ]]; then
|
| 80 |
+
cp "$binary_path" "release/${binary_name}-$target$extension"
|
| 81 |
+
info "完成构建 $target"
|
| 82 |
+
else
|
| 83 |
+
warn "构建产物未找到: $target"
|
| 84 |
+
warn "查找路径: $binary_path"
|
| 85 |
+
warn "当前目录内容:"
|
| 86 |
+
ls -R target/
|
| 87 |
+
return 1
|
| 88 |
+
fi
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
# 获取 CPU 架构和操作系统
|
| 92 |
+
ARCH=$(uname -m | sed 's/^aarch64\|arm64$/aarch64/;s/^x86_64\|x86-64\|x64\|amd64$/x86_64/')
|
| 93 |
+
OS=$(uname -s)
|
| 94 |
+
|
| 95 |
+
# 确定当前系统的目标平台
|
| 96 |
+
get_target() {
|
| 97 |
+
local arch=$1
|
| 98 |
+
local os=$2
|
| 99 |
+
case "$os" in
|
| 100 |
+
"Darwin") echo "${arch}-apple-darwin" ;;
|
| 101 |
+
"Linux")
|
| 102 |
+
if [[ $USE_STATIC == true ]]; then
|
| 103 |
+
echo "${arch}-unknown-linux-musl"
|
| 104 |
+
else
|
| 105 |
+
echo "${arch}-unknown-linux-gnu"
|
| 106 |
+
fi
|
| 107 |
+
;;
|
| 108 |
+
"MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") echo "${arch}-pc-windows-msvc" ;;
|
| 109 |
+
"FreeBSD") echo "${arch}-unknown-freebsd" ;;
|
| 110 |
+
*) error "不支持的系统: $os" ;;
|
| 111 |
+
esac
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
# 设置当前目标平台
|
| 115 |
+
CURRENT_TARGET=$(get_target "$ARCH" "$OS")
|
| 116 |
+
|
| 117 |
+
# 检查是否成功获取目标平台
|
| 118 |
+
[ -z "$CURRENT_TARGET" ] && error "无法确定当前系统的目标平台"
|
| 119 |
+
|
| 120 |
+
# 获取系统对应的所有目标
|
| 121 |
+
get_targets() {
|
| 122 |
+
case "$1" in
|
| 123 |
+
"linux")
|
| 124 |
+
# Linux 只构建当前架构
|
| 125 |
+
echo "$CURRENT_TARGET"
|
| 126 |
+
;;
|
| 127 |
+
"freebsd")
|
| 128 |
+
# FreeBSD 只构建当前架构
|
| 129 |
+
echo "$CURRENT_TARGET"
|
| 130 |
+
;;
|
| 131 |
+
"windows")
|
| 132 |
+
# Windows 只构建当前架构
|
| 133 |
+
echo "$CURRENT_TARGET"
|
| 134 |
+
;;
|
| 135 |
+
"macos")
|
| 136 |
+
# macOS 构建所有 macOS 目标
|
| 137 |
+
echo "x86_64-apple-darwin aarch64-apple-darwin"
|
| 138 |
+
;;
|
| 139 |
+
*) error "不支持的系统组: $1" ;;
|
| 140 |
+
esac
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
# 检查依赖
|
| 144 |
+
check_requirements
|
| 145 |
+
|
| 146 |
+
# 确定要构建的目标
|
| 147 |
+
case "$OS" in
|
| 148 |
+
Darwin)
|
| 149 |
+
TARGETS=($(get_targets "macos"))
|
| 150 |
+
;;
|
| 151 |
+
Linux)
|
| 152 |
+
TARGETS=($(get_targets "linux"))
|
| 153 |
+
;;
|
| 154 |
+
FreeBSD)
|
| 155 |
+
TARGETS=($(get_targets "freebsd"))
|
| 156 |
+
;;
|
| 157 |
+
MINGW*|MSYS*|CYGWIN*|Windows_NT)
|
| 158 |
+
TARGETS=($(get_targets "windows"))
|
| 159 |
+
;;
|
| 160 |
+
*) error "不支持的系统: $OS" ;;
|
| 161 |
+
esac
|
| 162 |
+
|
| 163 |
+
# 创建 release 目录
|
| 164 |
+
mkdir -p release
|
| 165 |
+
|
| 166 |
+
# 设置静态链接标志
|
| 167 |
+
RUSTFLAGS="-C link-arg=-s"
|
| 168 |
+
[[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-s"
|
| 169 |
+
|
| 170 |
+
# 并行构建所有目标
|
| 171 |
+
info "开始构建..."
|
| 172 |
+
for target in "${TARGETS[@]}"; do
|
| 173 |
+
build_target "$target" "$RUSTFLAGS" &
|
| 174 |
+
done
|
| 175 |
+
|
| 176 |
+
# 等待所有构建完成
|
| 177 |
+
wait
|
| 178 |
+
|
| 179 |
+
# 为 macOS 平台创建通用二进制
|
| 180 |
+
if [[ "$OS" == "Darwin" ]] && [[ ${#TARGETS[@]} -gt 1 ]]; then
|
| 181 |
+
binary_suffix=""
|
| 182 |
+
[[ $USE_STATIC == true ]] && binary_suffix="-static"
|
| 183 |
+
|
| 184 |
+
if [[ -f "release/cursor-api${binary_suffix}-x86_64-apple-darwin" ]] && \
|
| 185 |
+
[[ -f "release/cursor-api${binary_suffix}-aarch64-apple-darwin" ]]; then
|
| 186 |
+
info "创建 macOS 通用二进制..."
|
| 187 |
+
lipo -create \
|
| 188 |
+
"release/cursor-api${binary_suffix}-x86_64-apple-darwin" \
|
| 189 |
+
"release/cursor-api${binary_suffix}-aarch64-apple-darwin" \
|
| 190 |
+
-output "release/cursor-api${binary_suffix}-universal-apple-darwin"
|
| 191 |
+
fi
|
| 192 |
+
fi
|
| 193 |
+
|
| 194 |
+
info "构建完成!"
|
scripts/minify.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
|
| 3 |
+
const { minify: minifyHtml } = require('html-minifier-terser');
|
| 4 |
+
const { minify: minifyJs } = require('terser');
|
| 5 |
+
const CleanCSS = require('clean-css');
|
| 6 |
+
const MarkdownIt = require('markdown-it');
|
| 7 |
+
const fs = require('fs');
|
| 8 |
+
const path = require('path');
|
| 9 |
+
|
| 10 |
+
// 配置选项
|
| 11 |
+
const options = {
|
| 12 |
+
collapseWhitespace: true,
|
| 13 |
+
removeComments: true,
|
| 14 |
+
removeEmptyAttributes: true,
|
| 15 |
+
removeOptionalTags: true,
|
| 16 |
+
removeRedundantAttributes: true,
|
| 17 |
+
removeScriptTypeAttributes: true,
|
| 18 |
+
removeStyleLinkTypeAttributes: true,
|
| 19 |
+
minifyCSS: true,
|
| 20 |
+
minifyJS: true,
|
| 21 |
+
processScripts: ['application/json'],
|
| 22 |
+
};
|
| 23 |
+
|
| 24 |
+
// CSS 压缩选项
|
| 25 |
+
const cssOptions = {
|
| 26 |
+
level: 2
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
// 处理文件
|
| 30 |
+
async function minifyFile(inputPath, outputPath) {
|
| 31 |
+
try {
|
| 32 |
+
let ext = path.extname(inputPath).toLowerCase();
|
| 33 |
+
if (ext === '.md') ext = '.html';
|
| 34 |
+
const filename = path.basename(inputPath);
|
| 35 |
+
let content = fs.readFileSync(inputPath, 'utf8');
|
| 36 |
+
let minified;
|
| 37 |
+
|
| 38 |
+
// 特殊处理 readme.html
|
| 39 |
+
if (filename.toLowerCase() === 'readme.md') {
|
| 40 |
+
const md = new MarkdownIt({
|
| 41 |
+
html: true,
|
| 42 |
+
linkify: true,
|
| 43 |
+
typographer: true
|
| 44 |
+
});
|
| 45 |
+
const readmeMdPath = path.join(__dirname, '..', 'README.md');
|
| 46 |
+
const markdownContent = fs.readFileSync(readmeMdPath, 'utf8');
|
| 47 |
+
// 添加基本的 markdown 样式
|
| 48 |
+
const htmlContent = `
|
| 49 |
+
<!DOCTYPE html>
|
| 50 |
+
<html>
|
| 51 |
+
<head>
|
| 52 |
+
<meta charset="UTF-8">
|
| 53 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 54 |
+
<title>README</title>
|
| 55 |
+
<style>
|
| 56 |
+
body {
|
| 57 |
+
max-width: 800px;
|
| 58 |
+
margin: 0 auto;
|
| 59 |
+
padding: 20px;
|
| 60 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
| 61 |
+
line-height: 1.6;
|
| 62 |
+
}
|
| 63 |
+
pre {
|
| 64 |
+
background-color: #f6f8fa;
|
| 65 |
+
padding: 16px;
|
| 66 |
+
border-radius: 6px;
|
| 67 |
+
overflow: auto;
|
| 68 |
+
}
|
| 69 |
+
code {
|
| 70 |
+
background-color: #f6f8fa;
|
| 71 |
+
padding: 0.2em 0.4em;
|
| 72 |
+
border-radius: 3px;
|
| 73 |
+
}
|
| 74 |
+
img {
|
| 75 |
+
max-width: 100%;
|
| 76 |
+
}
|
| 77 |
+
table {
|
| 78 |
+
border-collapse: collapse;
|
| 79 |
+
width: 100%;
|
| 80 |
+
}
|
| 81 |
+
table td, table th {
|
| 82 |
+
border: 1px solid #dfe2e5;
|
| 83 |
+
padding: 6px 13px;
|
| 84 |
+
}
|
| 85 |
+
blockquote {
|
| 86 |
+
border-left: 4px solid #dfe2e5;
|
| 87 |
+
margin: 0;
|
| 88 |
+
padding: 0 1em;
|
| 89 |
+
color: #6a737d;
|
| 90 |
+
}
|
| 91 |
+
</style>
|
| 92 |
+
</head>
|
| 93 |
+
<body>
|
| 94 |
+
${md.render(markdownContent)}
|
| 95 |
+
</body>
|
| 96 |
+
</html>
|
| 97 |
+
`;
|
| 98 |
+
content = htmlContent;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
switch (ext) {
|
| 102 |
+
case '.html':
|
| 103 |
+
minified = await minifyHtml(content, options);
|
| 104 |
+
break;
|
| 105 |
+
case '.js':
|
| 106 |
+
const result = await minifyJs(content);
|
| 107 |
+
minified = result.code;
|
| 108 |
+
break;
|
| 109 |
+
case '.css':
|
| 110 |
+
minified = new CleanCSS(cssOptions).minify(content).styles;
|
| 111 |
+
break;
|
| 112 |
+
default:
|
| 113 |
+
throw new Error(`Unsupported file type: ${ext}`);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
fs.writeFileSync(outputPath, minified);
|
| 117 |
+
console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`);
|
| 118 |
+
} catch (err) {
|
| 119 |
+
console.error(`✗ Error processing ${inputPath}:`, err);
|
| 120 |
+
process.exit(1);
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// 主函数
|
| 125 |
+
async function main() {
|
| 126 |
+
// 获取命令行参数,跳过前两个参数(node和脚本路径)
|
| 127 |
+
const files = process.argv.slice(2);
|
| 128 |
+
|
| 129 |
+
if (files.length === 0) {
|
| 130 |
+
console.error('No input files specified');
|
| 131 |
+
process.exit(1);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
const staticDir = path.join(__dirname, '..', 'static');
|
| 135 |
+
|
| 136 |
+
for (const file of files) {
|
| 137 |
+
// 特殊处理 README.md 的输入路径
|
| 138 |
+
let inputPath;
|
| 139 |
+
let outputPath;
|
| 140 |
+
|
| 141 |
+
if (file.toLowerCase() === 'readme.md') {
|
| 142 |
+
inputPath = path.join(__dirname, '..', 'README.md');
|
| 143 |
+
outputPath = path.join(staticDir, 'readme.min.html');
|
| 144 |
+
} else {
|
| 145 |
+
inputPath = path.join(staticDir, file);
|
| 146 |
+
const ext = path.extname(file);
|
| 147 |
+
outputPath = path.join(
|
| 148 |
+
staticDir,
|
| 149 |
+
file.replace(ext, `.min${ext}`)
|
| 150 |
+
);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
await minifyFile(inputPath, outputPath);
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
main();
|
scripts/package-lock.json
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "html-minifier-scripts",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "html-minifier-scripts",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"clean-css": "^5.3.3",
|
| 12 |
+
"html-minifier-terser": "^7.2.0",
|
| 13 |
+
"markdown-it": "^14.1.0",
|
| 14 |
+
"terser": "^5.37.0"
|
| 15 |
+
},
|
| 16 |
+
"engines": {
|
| 17 |
+
"node": ">=14.0.0"
|
| 18 |
+
}
|
| 19 |
+
},
|
| 20 |
+
"node_modules/@jridgewell/gen-mapping": {
|
| 21 |
+
"version": "0.3.8",
|
| 22 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
| 23 |
+
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
| 24 |
+
"license": "MIT",
|
| 25 |
+
"dependencies": {
|
| 26 |
+
"@jridgewell/set-array": "^1.2.1",
|
| 27 |
+
"@jridgewell/sourcemap-codec": "^1.4.10",
|
| 28 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 29 |
+
},
|
| 30 |
+
"engines": {
|
| 31 |
+
"node": ">=6.0.0"
|
| 32 |
+
}
|
| 33 |
+
},
|
| 34 |
+
"node_modules/@jridgewell/resolve-uri": {
|
| 35 |
+
"version": "3.1.2",
|
| 36 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 37 |
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 38 |
+
"license": "MIT",
|
| 39 |
+
"engines": {
|
| 40 |
+
"node": ">=6.0.0"
|
| 41 |
+
}
|
| 42 |
+
},
|
| 43 |
+
"node_modules/@jridgewell/set-array": {
|
| 44 |
+
"version": "1.2.1",
|
| 45 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
| 46 |
+
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
| 47 |
+
"license": "MIT",
|
| 48 |
+
"engines": {
|
| 49 |
+
"node": ">=6.0.0"
|
| 50 |
+
}
|
| 51 |
+
},
|
| 52 |
+
"node_modules/@jridgewell/source-map": {
|
| 53 |
+
"version": "0.3.6",
|
| 54 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
| 55 |
+
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
| 56 |
+
"license": "MIT",
|
| 57 |
+
"dependencies": {
|
| 58 |
+
"@jridgewell/gen-mapping": "^0.3.5",
|
| 59 |
+
"@jridgewell/trace-mapping": "^0.3.25"
|
| 60 |
+
}
|
| 61 |
+
},
|
| 62 |
+
"node_modules/@jridgewell/sourcemap-codec": {
|
| 63 |
+
"version": "1.5.0",
|
| 64 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
| 65 |
+
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
| 66 |
+
"license": "MIT"
|
| 67 |
+
},
|
| 68 |
+
"node_modules/@jridgewell/trace-mapping": {
|
| 69 |
+
"version": "0.3.25",
|
| 70 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
| 71 |
+
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
| 72 |
+
"license": "MIT",
|
| 73 |
+
"dependencies": {
|
| 74 |
+
"@jridgewell/resolve-uri": "^3.1.0",
|
| 75 |
+
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 76 |
+
}
|
| 77 |
+
},
|
| 78 |
+
"node_modules/acorn": {
|
| 79 |
+
"version": "8.14.0",
|
| 80 |
+
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
| 81 |
+
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
| 82 |
+
"license": "MIT",
|
| 83 |
+
"bin": {
|
| 84 |
+
"acorn": "bin/acorn"
|
| 85 |
+
},
|
| 86 |
+
"engines": {
|
| 87 |
+
"node": ">=0.4.0"
|
| 88 |
+
}
|
| 89 |
+
},
|
| 90 |
+
"node_modules/argparse": {
|
| 91 |
+
"version": "2.0.1",
|
| 92 |
+
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
| 93 |
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
| 94 |
+
"license": "Python-2.0"
|
| 95 |
+
},
|
| 96 |
+
"node_modules/buffer-from": {
|
| 97 |
+
"version": "1.1.2",
|
| 98 |
+
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
| 99 |
+
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
| 100 |
+
"license": "MIT"
|
| 101 |
+
},
|
| 102 |
+
"node_modules/camel-case": {
|
| 103 |
+
"version": "4.1.2",
|
| 104 |
+
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
|
| 105 |
+
"integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
|
| 106 |
+
"license": "MIT",
|
| 107 |
+
"dependencies": {
|
| 108 |
+
"pascal-case": "^3.1.2",
|
| 109 |
+
"tslib": "^2.0.3"
|
| 110 |
+
}
|
| 111 |
+
},
|
| 112 |
+
"node_modules/clean-css": {
|
| 113 |
+
"version": "5.3.3",
|
| 114 |
+
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
| 115 |
+
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
|
| 116 |
+
"license": "MIT",
|
| 117 |
+
"dependencies": {
|
| 118 |
+
"source-map": "~0.6.0"
|
| 119 |
+
},
|
| 120 |
+
"engines": {
|
| 121 |
+
"node": ">= 10.0"
|
| 122 |
+
}
|
| 123 |
+
},
|
| 124 |
+
"node_modules/commander": {
|
| 125 |
+
"version": "10.0.1",
|
| 126 |
+
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
| 127 |
+
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
| 128 |
+
"license": "MIT",
|
| 129 |
+
"engines": {
|
| 130 |
+
"node": ">=14"
|
| 131 |
+
}
|
| 132 |
+
},
|
| 133 |
+
"node_modules/dot-case": {
|
| 134 |
+
"version": "3.0.4",
|
| 135 |
+
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
| 136 |
+
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
| 137 |
+
"license": "MIT",
|
| 138 |
+
"dependencies": {
|
| 139 |
+
"no-case": "^3.0.4",
|
| 140 |
+
"tslib": "^2.0.3"
|
| 141 |
+
}
|
| 142 |
+
},
|
| 143 |
+
"node_modules/entities": {
|
| 144 |
+
"version": "4.5.0",
|
| 145 |
+
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
| 146 |
+
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
| 147 |
+
"license": "BSD-2-Clause",
|
| 148 |
+
"engines": {
|
| 149 |
+
"node": ">=0.12"
|
| 150 |
+
},
|
| 151 |
+
"funding": {
|
| 152 |
+
"url": "https://github.com/fb55/entities?sponsor=1"
|
| 153 |
+
}
|
| 154 |
+
},
|
| 155 |
+
"node_modules/html-minifier-terser": {
|
| 156 |
+
"version": "7.2.0",
|
| 157 |
+
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
|
| 158 |
+
"integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
|
| 159 |
+
"license": "MIT",
|
| 160 |
+
"dependencies": {
|
| 161 |
+
"camel-case": "^4.1.2",
|
| 162 |
+
"clean-css": "~5.3.2",
|
| 163 |
+
"commander": "^10.0.0",
|
| 164 |
+
"entities": "^4.4.0",
|
| 165 |
+
"param-case": "^3.0.4",
|
| 166 |
+
"relateurl": "^0.2.7",
|
| 167 |
+
"terser": "^5.15.1"
|
| 168 |
+
},
|
| 169 |
+
"bin": {
|
| 170 |
+
"html-minifier-terser": "cli.js"
|
| 171 |
+
},
|
| 172 |
+
"engines": {
|
| 173 |
+
"node": "^14.13.1 || >=16.0.0"
|
| 174 |
+
}
|
| 175 |
+
},
|
| 176 |
+
"node_modules/linkify-it": {
|
| 177 |
+
"version": "5.0.0",
|
| 178 |
+
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
| 179 |
+
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
| 180 |
+
"license": "MIT",
|
| 181 |
+
"dependencies": {
|
| 182 |
+
"uc.micro": "^2.0.0"
|
| 183 |
+
}
|
| 184 |
+
},
|
| 185 |
+
"node_modules/lower-case": {
|
| 186 |
+
"version": "2.0.2",
|
| 187 |
+
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
| 188 |
+
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
| 189 |
+
"license": "MIT",
|
| 190 |
+
"dependencies": {
|
| 191 |
+
"tslib": "^2.0.3"
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
"node_modules/markdown-it": {
|
| 195 |
+
"version": "14.1.0",
|
| 196 |
+
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
|
| 197 |
+
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
|
| 198 |
+
"license": "MIT",
|
| 199 |
+
"dependencies": {
|
| 200 |
+
"argparse": "^2.0.1",
|
| 201 |
+
"entities": "^4.4.0",
|
| 202 |
+
"linkify-it": "^5.0.0",
|
| 203 |
+
"mdurl": "^2.0.0",
|
| 204 |
+
"punycode.js": "^2.3.1",
|
| 205 |
+
"uc.micro": "^2.1.0"
|
| 206 |
+
},
|
| 207 |
+
"bin": {
|
| 208 |
+
"markdown-it": "bin/markdown-it.mjs"
|
| 209 |
+
}
|
| 210 |
+
},
|
| 211 |
+
"node_modules/mdurl": {
|
| 212 |
+
"version": "2.0.0",
|
| 213 |
+
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
| 214 |
+
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
| 215 |
+
"license": "MIT"
|
| 216 |
+
},
|
| 217 |
+
"node_modules/no-case": {
|
| 218 |
+
"version": "3.0.4",
|
| 219 |
+
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
| 220 |
+
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
| 221 |
+
"license": "MIT",
|
| 222 |
+
"dependencies": {
|
| 223 |
+
"lower-case": "^2.0.2",
|
| 224 |
+
"tslib": "^2.0.3"
|
| 225 |
+
}
|
| 226 |
+
},
|
| 227 |
+
"node_modules/param-case": {
|
| 228 |
+
"version": "3.0.4",
|
| 229 |
+
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
| 230 |
+
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
|
| 231 |
+
"license": "MIT",
|
| 232 |
+
"dependencies": {
|
| 233 |
+
"dot-case": "^3.0.4",
|
| 234 |
+
"tslib": "^2.0.3"
|
| 235 |
+
}
|
| 236 |
+
},
|
| 237 |
+
"node_modules/pascal-case": {
|
| 238 |
+
"version": "3.1.2",
|
| 239 |
+
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
| 240 |
+
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
| 241 |
+
"license": "MIT",
|
| 242 |
+
"dependencies": {
|
| 243 |
+
"no-case": "^3.0.4",
|
| 244 |
+
"tslib": "^2.0.3"
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
+
"node_modules/punycode.js": {
|
| 248 |
+
"version": "2.3.1",
|
| 249 |
+
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
| 250 |
+
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
| 251 |
+
"license": "MIT",
|
| 252 |
+
"engines": {
|
| 253 |
+
"node": ">=6"
|
| 254 |
+
}
|
| 255 |
+
},
|
| 256 |
+
"node_modules/relateurl": {
|
| 257 |
+
"version": "0.2.7",
|
| 258 |
+
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
| 259 |
+
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
|
| 260 |
+
"license": "MIT",
|
| 261 |
+
"engines": {
|
| 262 |
+
"node": ">= 0.10"
|
| 263 |
+
}
|
| 264 |
+
},
|
| 265 |
+
"node_modules/source-map": {
|
| 266 |
+
"version": "0.6.1",
|
| 267 |
+
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
| 268 |
+
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
| 269 |
+
"license": "BSD-3-Clause",
|
| 270 |
+
"engines": {
|
| 271 |
+
"node": ">=0.10.0"
|
| 272 |
+
}
|
| 273 |
+
},
|
| 274 |
+
"node_modules/source-map-support": {
|
| 275 |
+
"version": "0.5.21",
|
| 276 |
+
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
| 277 |
+
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
| 278 |
+
"license": "MIT",
|
| 279 |
+
"dependencies": {
|
| 280 |
+
"buffer-from": "^1.0.0",
|
| 281 |
+
"source-map": "^0.6.0"
|
| 282 |
+
}
|
| 283 |
+
},
|
| 284 |
+
"node_modules/terser": {
|
| 285 |
+
"version": "5.37.0",
|
| 286 |
+
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
|
| 287 |
+
"integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
|
| 288 |
+
"license": "BSD-2-Clause",
|
| 289 |
+
"dependencies": {
|
| 290 |
+
"@jridgewell/source-map": "^0.3.3",
|
| 291 |
+
"acorn": "^8.8.2",
|
| 292 |
+
"commander": "^2.20.0",
|
| 293 |
+
"source-map-support": "~0.5.20"
|
| 294 |
+
},
|
| 295 |
+
"bin": {
|
| 296 |
+
"terser": "bin/terser"
|
| 297 |
+
},
|
| 298 |
+
"engines": {
|
| 299 |
+
"node": ">=10"
|
| 300 |
+
}
|
| 301 |
+
},
|
| 302 |
+
"node_modules/terser/node_modules/commander": {
|
| 303 |
+
"version": "2.20.3",
|
| 304 |
+
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
| 305 |
+
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
| 306 |
+
"license": "MIT"
|
| 307 |
+
},
|
| 308 |
+
"node_modules/tslib": {
|
| 309 |
+
"version": "2.8.1",
|
| 310 |
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
| 311 |
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
| 312 |
+
"license": "0BSD"
|
| 313 |
+
},
|
| 314 |
+
"node_modules/uc.micro": {
|
| 315 |
+
"version": "2.1.0",
|
| 316 |
+
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
| 317 |
+
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
| 318 |
+
"license": "MIT"
|
| 319 |
+
}
|
| 320 |
+
}
|
| 321 |
+
}
|
scripts/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "html-minifier-scripts",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"private": true,
|
| 5 |
+
"engines": {
|
| 6 |
+
"node": ">=14.0.0"
|
| 7 |
+
},
|
| 8 |
+
"dependencies": {
|
| 9 |
+
"clean-css": "^5.3.3",
|
| 10 |
+
"html-minifier-terser": "^7.2.0",
|
| 11 |
+
"markdown-it": "^14.1.0",
|
| 12 |
+
"terser": "^5.37.0"
|
| 13 |
+
}
|
| 14 |
+
}
|
scripts/setup.ps1
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ���ô���ʱִֹͣ��
|
| 2 |
+
$ErrorActionPreference = "Stop"
|
| 3 |
+
$ProgressPreference = "SilentlyContinue" # �ӿ������ٶ�
|
| 4 |
+
|
| 5 |
+
# ��ɫ�������
|
| 6 |
+
function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
|
| 7 |
+
function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow }
|
| 8 |
+
function Write-Success { param($Message) Write-Host "[SUCCESS] $Message" -ForegroundColor Green }
|
| 9 |
+
function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 }
|
| 10 |
+
|
| 11 |
+
# ������ԱȨ��
|
| 12 |
+
function Test-Administrator {
|
| 13 |
+
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
| 14 |
+
$principal = New-Object Security.Principal.WindowsPrincipal $user
|
| 15 |
+
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
if (-not (Test-Administrator)) {
|
| 19 |
+
Write-Error "���Թ���ԱȨ�����д˽ű�"
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
# ������Ϣ
|
| 23 |
+
function Show-Help {
|
| 24 |
+
Write-Host @"
|
| 25 |
+
�÷�: $(Split-Path $MyInvocation.ScriptName -Leaf) [ѡ��]
|
| 26 |
+
|
| 27 |
+
ѡ��:
|
| 28 |
+
-NoVS ����װ Visual Studio Build Tools
|
| 29 |
+
-NoRust ����װ Rust
|
| 30 |
+
-NoNode ����װ Node.js
|
| 31 |
+
-Help ��ʾ�˰�����Ϣ
|
| 32 |
+
|
| 33 |
+
ʾ��:
|
| 34 |
+
.\setup.ps1
|
| 35 |
+
.\setup.ps1 -NoVS
|
| 36 |
+
.\setup.ps1 -NoRust -NoNode
|
| 37 |
+
"@
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# ��������
|
| 41 |
+
param(
|
| 42 |
+
[switch]$NoVS,
|
| 43 |
+
[switch]$NoRust,
|
| 44 |
+
[switch]$NoNode,
|
| 45 |
+
[switch]$Help
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
if ($Help) {
|
| 49 |
+
Show-Help
|
| 50 |
+
exit 0
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
# ��鲢��װ Chocolatey
|
| 54 |
+
function Install-Chocolatey {
|
| 55 |
+
Write-Info "��� Chocolatey..."
|
| 56 |
+
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
|
| 57 |
+
Write-Info "��װ Chocolatey..."
|
| 58 |
+
Set-ExecutionPolicy Bypass -Scope Process -Force
|
| 59 |
+
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
| 60 |
+
try {
|
| 61 |
+
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
| 62 |
+
}
|
| 63 |
+
catch {
|
| 64 |
+
Write-Error "��װ Chocolatey ʧ��: $_"
|
| 65 |
+
}
|
| 66 |
+
# ˢ�»�������
|
| 67 |
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
# ��װ Visual Studio Build Tools
|
| 72 |
+
function Install-VSBuildTools {
|
| 73 |
+
if ($NoVS) {
|
| 74 |
+
Write-Info "���� Visual Studio Build Tools ��װ"
|
| 75 |
+
return
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
Write-Info "��� Visual Studio Build Tools..."
|
| 79 |
+
$vsPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
| 80 |
+
if (-not (Test-Path $vsPath)) {
|
| 81 |
+
Write-Info "��װ Visual Studio Build Tools..."
|
| 82 |
+
try {
|
| 83 |
+
# ���ذ�װ����
|
| 84 |
+
$vsInstallerUrl = "https://aka.ms/vs/17/release/vs_BuildTools.exe"
|
| 85 |
+
$vsInstallerPath = "$env:TEMP\vs_BuildTools.exe"
|
| 86 |
+
Invoke-WebRequest -Uri $vsInstallerUrl -OutFile $vsInstallerPath
|
| 87 |
+
|
| 88 |
+
# ��װ
|
| 89 |
+
$process = Start-Process -FilePath $vsInstallerPath -ArgumentList `
|
| 90 |
+
"--quiet", "--wait", "--norestart", "--nocache", `
|
| 91 |
+
"--installPath", "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools", `
|
| 92 |
+
"--add", "Microsoft.VisualStudio.Workload.VCTools" `
|
| 93 |
+
-NoNewWindow -Wait -PassThru
|
| 94 |
+
|
| 95 |
+
if ($process.ExitCode -ne 0) {
|
| 96 |
+
Write-Error "Visual Studio Build Tools ��װʧ��"
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
Remove-Item $vsInstallerPath -Force
|
| 100 |
+
}
|
| 101 |
+
catch {
|
| 102 |
+
Write-Error "��װ Visual Studio Build Tools ʧ��: $_"
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
else {
|
| 106 |
+
Write-Info "Visual Studio Build Tools �Ѱ�װ"
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
# ��װ Rust
|
| 111 |
+
function Install-Rust {
|
| 112 |
+
if ($NoRust) {
|
| 113 |
+
Write-Info "���� Rust ��װ"
|
| 114 |
+
return
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
Write-Info "��� Rust..."
|
| 118 |
+
if (-not (Get-Command rustc -ErrorAction SilentlyContinue)) {
|
| 119 |
+
Write-Info "��װ Rust..."
|
| 120 |
+
try {
|
| 121 |
+
$rustupInit = "$env:TEMP\rustup-init.exe"
|
| 122 |
+
Invoke-WebRequest -Uri "https://win.rustup.rs" -OutFile $rustupInit
|
| 123 |
+
Start-Process -FilePath $rustupInit -ArgumentList "-y" -Wait
|
| 124 |
+
Remove-Item $rustupInit -Force
|
| 125 |
+
|
| 126 |
+
# ˢ�»�������
|
| 127 |
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
| 128 |
+
}
|
| 129 |
+
catch {
|
| 130 |
+
Write-Error "��װ Rust ʧ��: $_"
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
# ����Ŀ��ƽ̨
|
| 135 |
+
Write-Info "���� Rust Ŀ��ƽ̨..."
|
| 136 |
+
$arch = if ([Environment]::Is64BitOperatingSystem) { "x86_64" } else { "i686" }
|
| 137 |
+
rustup target add "$arch-pc-windows-msvc"
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
# ��װ��������
|
| 141 |
+
function Install-Tools {
|
| 142 |
+
Write-Info "��װ��Ҫ����..."
|
| 143 |
+
|
| 144 |
+
# ��װ protoc
|
| 145 |
+
if (-not (Get-Command protoc -ErrorAction SilentlyContinue)) {
|
| 146 |
+
Write-Info "��װ Protocol Buffers..."
|
| 147 |
+
choco install -y protoc
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
# ��װ Git
|
| 151 |
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
| 152 |
+
Write-Info "��װ Git..."
|
| 153 |
+
choco install -y git
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
# ��װ Node.js
|
| 157 |
+
if (-not $NoNode -and -not (Get-Command node -ErrorAction SilentlyContinue)) {
|
| 158 |
+
Write-Info "��װ Node.js..."
|
| 159 |
+
choco install -y nodejs
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
# ˢ�»�������
|
| 163 |
+
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
# ������
|
| 167 |
+
try {
|
| 168 |
+
Write-Info "��ʼ��װ��Ҫ���..."
|
| 169 |
+
|
| 170 |
+
Install-Chocolatey
|
| 171 |
+
Install-VSBuildTools
|
| 172 |
+
Install-Rust
|
| 173 |
+
Install-Tools
|
| 174 |
+
|
| 175 |
+
Write-Success "��װ��ɣ�"
|
| 176 |
+
}
|
| 177 |
+
catch {
|
| 178 |
+
Write-Error "��װ�����г��ִ���: $_"
|
| 179 |
+
}
|
scripts/setup.sh
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# 设置错误时退出
|
| 4 |
+
set -e
|
| 5 |
+
|
| 6 |
+
# 颜色输出
|
| 7 |
+
RED='\033[0;31m'
|
| 8 |
+
GREEN='\033[0;32m'
|
| 9 |
+
BLUE='\033[0;34m'
|
| 10 |
+
NC='\033[0m' # No Color
|
| 11 |
+
|
| 12 |
+
info() {
|
| 13 |
+
echo -e "${BLUE}[INFO] $1${NC}"
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
error() {
|
| 17 |
+
echo -e "${RED}[ERROR] $1${NC}"
|
| 18 |
+
exit 1
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
# 检查是否为 root 用户(FreeBSD 和 Linux)
|
| 22 |
+
if [ "$(uname)" != "Darwin" ] && [ "$EUID" -ne 0 ]; then
|
| 23 |
+
error "请使用 root 权限运行此脚本 (sudo ./setup.sh)"
|
| 24 |
+
fi
|
| 25 |
+
|
| 26 |
+
# 检测包管理器
|
| 27 |
+
if command -v brew &> /dev/null; then
|
| 28 |
+
PKG_MANAGER="brew"
|
| 29 |
+
info "检测到 macOS/Homebrew 系统"
|
| 30 |
+
elif command -v pkg &> /dev/null; then
|
| 31 |
+
PKG_MANAGER="pkg"
|
| 32 |
+
info "检测到 FreeBSD 系统"
|
| 33 |
+
elif command -v apt-get &> /dev/null; then
|
| 34 |
+
PKG_MANAGER="apt-get"
|
| 35 |
+
info "检测到 Debian/Ubuntu 系统"
|
| 36 |
+
elif command -v dnf &> /dev/null; then
|
| 37 |
+
PKG_MANAGER="dnf"
|
| 38 |
+
info "检测到 Fedora/RHEL 系统"
|
| 39 |
+
elif command -v yum &> /dev/null; then
|
| 40 |
+
PKG_MANAGER="yum"
|
| 41 |
+
info "检测到 CentOS 系统"
|
| 42 |
+
else
|
| 43 |
+
error "未检测到支持的包管理器"
|
| 44 |
+
fi
|
| 45 |
+
|
| 46 |
+
# 更新包管理器缓存
|
| 47 |
+
info "更新包管理器缓存..."
|
| 48 |
+
case $PKG_MANAGER in
|
| 49 |
+
"brew")
|
| 50 |
+
brew update
|
| 51 |
+
;;
|
| 52 |
+
"pkg")
|
| 53 |
+
pkg update
|
| 54 |
+
;;
|
| 55 |
+
*)
|
| 56 |
+
$PKG_MANAGER update -y
|
| 57 |
+
;;
|
| 58 |
+
esac
|
| 59 |
+
|
| 60 |
+
# 安装基础构建工具
|
| 61 |
+
info "安装基础构建工具..."
|
| 62 |
+
case $PKG_MANAGER in
|
| 63 |
+
"brew")
|
| 64 |
+
brew install \
|
| 65 |
+
protobuf \
|
| 66 |
+
pkg-config \
|
| 67 |
+
openssl \
|
| 68 |
+
curl \
|
| 69 |
+
git \
|
| 70 |
+
node
|
| 71 |
+
;;
|
| 72 |
+
"pkg")
|
| 73 |
+
pkg install -y \
|
| 74 |
+
gmake \
|
| 75 |
+
protobuf \
|
| 76 |
+
pkgconf \
|
| 77 |
+
openssl \
|
| 78 |
+
curl \
|
| 79 |
+
git \
|
| 80 |
+
node
|
| 81 |
+
;;
|
| 82 |
+
"apt-get")
|
| 83 |
+
$PKG_MANAGER install -y --no-install-recommends \
|
| 84 |
+
build-essential \
|
| 85 |
+
protobuf-compiler \
|
| 86 |
+
pkg-config \
|
| 87 |
+
libssl-dev \
|
| 88 |
+
ca-certificates \
|
| 89 |
+
curl \
|
| 90 |
+
tzdata \
|
| 91 |
+
git
|
| 92 |
+
;;
|
| 93 |
+
*)
|
| 94 |
+
$PKG_MANAGER install -y \
|
| 95 |
+
gcc \
|
| 96 |
+
gcc-c++ \
|
| 97 |
+
make \
|
| 98 |
+
protobuf-compiler \
|
| 99 |
+
pkg-config \
|
| 100 |
+
openssl-devel \
|
| 101 |
+
ca-certificates \
|
| 102 |
+
curl \
|
| 103 |
+
tzdata \
|
| 104 |
+
git
|
| 105 |
+
;;
|
| 106 |
+
esac
|
| 107 |
+
|
| 108 |
+
# 安装 Node.js 和 npm(如果还没有通过包管理器安装)
|
| 109 |
+
if ! command -v node &> /dev/null && [ "$PKG_MANAGER" != "brew" ] && [ "$PKG_MANAGER" != "pkg" ]; then
|
| 110 |
+
info "安装 Node.js 和 npm..."
|
| 111 |
+
if [ "$PKG_MANAGER" = "apt-get" ]; then
|
| 112 |
+
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
| 113 |
+
$PKG_MANAGER install -y nodejs
|
| 114 |
+
else
|
| 115 |
+
curl -fsSL https://rpm.nodesource.com/setup_lts.x | bash -
|
| 116 |
+
$PKG_MANAGER install -y nodejs
|
| 117 |
+
fi
|
| 118 |
+
fi
|
| 119 |
+
|
| 120 |
+
# 安装 Rust(如果未安装)
|
| 121 |
+
if ! command -v rustc &> /dev/null; then
|
| 122 |
+
info "安装 Rust..."
|
| 123 |
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
| 124 |
+
. "$HOME/.cargo/env"
|
| 125 |
+
fi
|
| 126 |
+
|
| 127 |
+
# 添加目标平台
|
| 128 |
+
info "添加 Rust 目标平台..."
|
| 129 |
+
case "$(uname)" in
|
| 130 |
+
"FreeBSD")
|
| 131 |
+
rustup target add x86_64-unknown-freebsd
|
| 132 |
+
;;
|
| 133 |
+
"Darwin")
|
| 134 |
+
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
| 135 |
+
;;
|
| 136 |
+
*)
|
| 137 |
+
rustup target add x86_64-unknown-linux-gnu
|
| 138 |
+
;;
|
| 139 |
+
esac
|
| 140 |
+
|
| 141 |
+
# 清理包管理器缓存
|
| 142 |
+
case $PKG_MANAGER in
|
| 143 |
+
"apt-get")
|
| 144 |
+
rm -rf /var/lib/apt/lists/*
|
| 145 |
+
;;
|
| 146 |
+
"pkg")
|
| 147 |
+
pkg clean -y
|
| 148 |
+
;;
|
| 149 |
+
esac
|
| 150 |
+
|
| 151 |
+
# 设置时区(除了 macOS)
|
| 152 |
+
if [ "$(uname)" != "Darwin" ]; then
|
| 153 |
+
info "设置时区为 Asia/Shanghai..."
|
| 154 |
+
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
| 155 |
+
fi
|
| 156 |
+
|
| 157 |
+
echo -e "${GREEN}安装完成!${NC}"
|
serve.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Deno.serve(async(r:Request)=>{const rs=(s:number,m:string)=>new Response(m,{status:s,headers:{"Access-Control-Allow-Origin":"*"}});const h=r.headers.get("x-co");if(!h)return rs(400,"Missing header");const a=["api2.cursor.sh","www.cursor.com"];if(!a.includes(h))return rs(403,"Host denied");const u=new URL(r.url),p=["/aiserver.v1.AiService/StreamChat","/aiserver.v1.AiService/StreamChatWeb","/auth/full_stripe_profile","/api/usage","/api/auth/me"];if(!p.includes(u.pathname))return rs(404,"Path invalid");const hd=new Headers(r.headers);hd.delete("x-co");hd.set("Host",h);try{const f=await fetch(`https://${h}${u.pathname}${u.search}`,{method:r.method,headers:hd,body:r.body});const fh=new Headers(f.headers);fh.set("Access-Control-Allow-Origin","*");return new Response(f.body,{status:f.status,headers:fh})}catch(e){return rs(500,"Server error")}});
|
src/app.rs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pub mod config;
|
| 2 |
+
pub mod constant;
|
| 3 |
+
pub mod model;
|
| 4 |
+
pub mod lazy;
|
src/app/config.rs
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use super::{constant::AUTHORIZATION_BEARER_PREFIX, lazy::AUTH_TOKEN, model::AppConfig};
|
| 2 |
+
use crate::common::model::{
|
| 3 |
+
config::{ConfigData, ConfigUpdateRequest},
|
| 4 |
+
ApiStatus, ErrorResponse, NormalResponse,
|
| 5 |
+
};
|
| 6 |
+
use axum::{
|
| 7 |
+
http::{header::AUTHORIZATION, HeaderMap, StatusCode},
|
| 8 |
+
Json,
|
| 9 |
+
};
|
| 10 |
+
|
| 11 |
+
// 定义处理更新操作的宏
|
| 12 |
+
macro_rules! handle_updates {
|
| 13 |
+
($request:expr, $($field:ident => $update_fn:expr),* $(,)?) => {
|
| 14 |
+
$(
|
| 15 |
+
if let Some(value) = $request.$field {
|
| 16 |
+
$update_fn(value);
|
| 17 |
+
}
|
| 18 |
+
)*
|
| 19 |
+
};
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
// 定义处理重置操作的宏
|
| 23 |
+
macro_rules! handle_resets {
|
| 24 |
+
($request:expr, $($field:ident => $reset_fn:expr),* $(,)?) => {
|
| 25 |
+
$(
|
| 26 |
+
if $request.$field.is_some() {
|
| 27 |
+
$reset_fn();
|
| 28 |
+
}
|
| 29 |
+
)*
|
| 30 |
+
};
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
pub async fn handle_config_update(
|
| 34 |
+
headers: HeaderMap,
|
| 35 |
+
Json(request): Json<ConfigUpdateRequest>,
|
| 36 |
+
) -> Result<Json<NormalResponse<ConfigData>>, (StatusCode, Json<ErrorResponse>)> {
|
| 37 |
+
let auth_header = headers
|
| 38 |
+
.get(AUTHORIZATION)
|
| 39 |
+
.and_then(|h| h.to_str().ok())
|
| 40 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 41 |
+
.ok_or((
|
| 42 |
+
StatusCode::UNAUTHORIZED,
|
| 43 |
+
Json(ErrorResponse {
|
| 44 |
+
status: ApiStatus::Failed,
|
| 45 |
+
code: Some(401),
|
| 46 |
+
error: Some("未提供认证令牌".to_string()),
|
| 47 |
+
message: None,
|
| 48 |
+
}),
|
| 49 |
+
))?;
|
| 50 |
+
|
| 51 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 52 |
+
return Err((
|
| 53 |
+
StatusCode::UNAUTHORIZED,
|
| 54 |
+
Json(ErrorResponse {
|
| 55 |
+
status: ApiStatus::Failed,
|
| 56 |
+
code: Some(401),
|
| 57 |
+
error: Some("无效的认证令牌".to_string()),
|
| 58 |
+
message: None,
|
| 59 |
+
}),
|
| 60 |
+
));
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
match request.action.as_str() {
|
| 64 |
+
"get" => Ok(Json(NormalResponse {
|
| 65 |
+
status: ApiStatus::Success,
|
| 66 |
+
data: Some(ConfigData {
|
| 67 |
+
page_content: AppConfig::get_page_content(&request.path),
|
| 68 |
+
vision_ability: AppConfig::get_vision_ability(),
|
| 69 |
+
enable_slow_pool: AppConfig::get_slow_pool(),
|
| 70 |
+
enable_all_claude: AppConfig::get_allow_claude(),
|
| 71 |
+
usage_check_models: AppConfig::get_usage_check(),
|
| 72 |
+
enable_dynamic_key: AppConfig::get_dynamic_key(),
|
| 73 |
+
share_token: AppConfig::get_share_token(),
|
| 74 |
+
proxies: AppConfig::get_proxies(),
|
| 75 |
+
include_web_references: AppConfig::get_web_refs(),
|
| 76 |
+
}),
|
| 77 |
+
message: None,
|
| 78 |
+
})),
|
| 79 |
+
|
| 80 |
+
"update" => {
|
| 81 |
+
// 处理页面内容更新
|
| 82 |
+
if !request.path.is_empty() && request.content.is_some() {
|
| 83 |
+
let content = request.content.unwrap();
|
| 84 |
+
if let Err(e) = AppConfig::update_page_content(&request.path, content) {
|
| 85 |
+
return Err((
|
| 86 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 87 |
+
Json(ErrorResponse {
|
| 88 |
+
status: ApiStatus::Failed,
|
| 89 |
+
code: Some(500),
|
| 90 |
+
error: Some(format!("更新页面内容失败: {}", e)),
|
| 91 |
+
message: None,
|
| 92 |
+
}),
|
| 93 |
+
));
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
handle_updates!(request,
|
| 98 |
+
vision_ability => AppConfig::update_vision_ability,
|
| 99 |
+
enable_slow_pool => AppConfig::update_slow_pool,
|
| 100 |
+
enable_all_claude => AppConfig::update_allow_claude,
|
| 101 |
+
usage_check_models => AppConfig::update_usage_check,
|
| 102 |
+
enable_dynamic_key => AppConfig::update_dynamic_key,
|
| 103 |
+
share_token => AppConfig::update_share_token,
|
| 104 |
+
proxies => AppConfig::update_proxies,
|
| 105 |
+
include_web_references => AppConfig::update_web_refs,
|
| 106 |
+
);
|
| 107 |
+
|
| 108 |
+
Ok(Json(NormalResponse {
|
| 109 |
+
status: ApiStatus::Success,
|
| 110 |
+
data: None,
|
| 111 |
+
message: Some("配置已更新".to_string()),
|
| 112 |
+
}))
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
"reset" => {
|
| 116 |
+
// 重置页面内容
|
| 117 |
+
if !request.path.is_empty() {
|
| 118 |
+
if let Err(e) = AppConfig::reset_page_content(&request.path) {
|
| 119 |
+
return Err((
|
| 120 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 121 |
+
Json(ErrorResponse {
|
| 122 |
+
status: ApiStatus::Failed,
|
| 123 |
+
code: Some(500),
|
| 124 |
+
error: Some(format!("重置页面内容失败: {}", e)),
|
| 125 |
+
message: None,
|
| 126 |
+
}),
|
| 127 |
+
));
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
handle_resets!(request,
|
| 132 |
+
vision_ability => AppConfig::reset_vision_ability,
|
| 133 |
+
enable_slow_pool => AppConfig::reset_slow_pool,
|
| 134 |
+
enable_all_claude => AppConfig::reset_allow_claude,
|
| 135 |
+
usage_check_models => AppConfig::reset_usage_check,
|
| 136 |
+
enable_dynamic_key => AppConfig::reset_dynamic_key,
|
| 137 |
+
share_token => AppConfig::reset_share_token,
|
| 138 |
+
proxies => AppConfig::reset_proxies,
|
| 139 |
+
include_web_references => AppConfig::reset_web_refs,
|
| 140 |
+
);
|
| 141 |
+
|
| 142 |
+
Ok(Json(NormalResponse {
|
| 143 |
+
status: ApiStatus::Success,
|
| 144 |
+
data: None,
|
| 145 |
+
message: Some("配置已重置".to_string()),
|
| 146 |
+
}))
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
_ => Err((
|
| 150 |
+
StatusCode::BAD_REQUEST,
|
| 151 |
+
Json(ErrorResponse {
|
| 152 |
+
status: ApiStatus::Failed,
|
| 153 |
+
code: Some(400),
|
| 154 |
+
error: Some("无效的操作类型".to_string()),
|
| 155 |
+
message: None,
|
| 156 |
+
}),
|
| 157 |
+
)),
|
| 158 |
+
}
|
| 159 |
+
}
|
src/app/constant.rs
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
macro_rules! def_pub_const {
|
| 2 |
+
($name:ident, $value:expr) => {
|
| 3 |
+
pub const $name: &'static str = $value;
|
| 4 |
+
};
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
pub const COMMA: char = ',';
|
| 8 |
+
|
| 9 |
+
def_pub_const!(PKG_VERSION, env!("CARGO_PKG_VERSION"));
|
| 10 |
+
// def_pub_const!(PKG_NAME, env!("CARGO_PKG_NAME"));
|
| 11 |
+
// def_pub_const!(PKG_DESCRIPTION, env!("CARGO_PKG_DESCRIPTION"));
|
| 12 |
+
// def_pub_const!(PKG_AUTHORS, env!("CARGO_PKG_AUTHORS"));
|
| 13 |
+
// def_pub_const!(PKG_REPOSITORY, env!("CARGO_PKG_REPOSITORY"));
|
| 14 |
+
|
| 15 |
+
def_pub_const!(EMPTY_STRING, "");
|
| 16 |
+
|
| 17 |
+
def_pub_const!(COMMA_STRING, ",");
|
| 18 |
+
|
| 19 |
+
def_pub_const!(ROUTE_ROOT_PATH, "/");
|
| 20 |
+
def_pub_const!(ROUTE_HEALTH_PATH, "/health");
|
| 21 |
+
def_pub_const!(ROUTE_GET_HASH, "/get-hash");
|
| 22 |
+
def_pub_const!(ROUTE_GET_CHECKSUM, "/get-checksum");
|
| 23 |
+
def_pub_const!(ROUTE_GET_TIMESTAMP_HEADER, "/get-tsheader");
|
| 24 |
+
def_pub_const!(ROUTE_USER_INFO_PATH, "/userinfo");
|
| 25 |
+
def_pub_const!(ROUTE_API_PATH, "/api");
|
| 26 |
+
def_pub_const!(ROUTE_LOGS_PATH, "/logs");
|
| 27 |
+
def_pub_const!(ROUTE_CONFIG_PATH, "/config");
|
| 28 |
+
def_pub_const!(ROUTE_TOKENS_PATH, "/tokens");
|
| 29 |
+
def_pub_const!(ROUTE_TOKENS_GET_PATH, "/tokens/get");
|
| 30 |
+
def_pub_const!(ROUTE_TOKENS_RELOAD_PATH, "/tokens/reload");
|
| 31 |
+
def_pub_const!(ROUTE_TOKENS_UPDATE_PATH, "/tokens/update");
|
| 32 |
+
def_pub_const!(ROUTE_TOKENS_ADD_PATH, "/tokens/add");
|
| 33 |
+
def_pub_const!(ROUTE_TOKENS_DELETE_PATH, "/tokens/delete");
|
| 34 |
+
def_pub_const!(ROUTE_ENV_EXAMPLE_PATH, "/env-example");
|
| 35 |
+
def_pub_const!(ROUTE_STATIC_PATH, "/static/{path}");
|
| 36 |
+
def_pub_const!(ROUTE_SHARED_STYLES_PATH, "/static/shared-styles.css");
|
| 37 |
+
def_pub_const!(ROUTE_SHARED_JS_PATH, "/static/shared.js");
|
| 38 |
+
def_pub_const!(ROUTE_ABOUT_PATH, "/about");
|
| 39 |
+
def_pub_const!(ROUTE_README_PATH, "/readme");
|
| 40 |
+
def_pub_const!(ROUTE_BASIC_CALIBRATION_PATH, "/basic-calibration");
|
| 41 |
+
def_pub_const!(ROUTE_BUILD_KEY_PATH, "/build-key");
|
| 42 |
+
|
| 43 |
+
def_pub_const!(DEFAULT_TOKEN_LIST_FILE_NAME, ".tokens");
|
| 44 |
+
|
| 45 |
+
def_pub_const!(STATUS_PENDING, "pending");
|
| 46 |
+
def_pub_const!(STATUS_SUCCESS, "success");
|
| 47 |
+
def_pub_const!(STATUS_FAILED, "failed");
|
| 48 |
+
|
| 49 |
+
def_pub_const!(HEADER_NAME_GHOST_MODE, "x-ghost-mode");
|
| 50 |
+
|
| 51 |
+
def_pub_const!(TRUE, "true");
|
| 52 |
+
def_pub_const!(FALSE, "false");
|
| 53 |
+
|
| 54 |
+
// def_pub_const!(CONTENT_TYPE_PROTO, "application/proto");
|
| 55 |
+
def_pub_const!(CONTENT_TYPE_CONNECT_PROTO, "application/connect+proto");
|
| 56 |
+
def_pub_const!(CONTENT_TYPE_TEXT_HTML_WITH_UTF8, "text/html;charset=utf-8");
|
| 57 |
+
def_pub_const!(
|
| 58 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8,
|
| 59 |
+
"text/plain;charset=utf-8"
|
| 60 |
+
);
|
| 61 |
+
def_pub_const!(CONTENT_TYPE_TEXT_CSS_WITH_UTF8, "text/css;charset=utf-8");
|
| 62 |
+
def_pub_const!(
|
| 63 |
+
CONTENT_TYPE_TEXT_JS_WITH_UTF8,
|
| 64 |
+
"text/javascript;charset=utf-8"
|
| 65 |
+
);
|
| 66 |
+
|
| 67 |
+
def_pub_const!(AUTHORIZATION_BEARER_PREFIX, "Bearer ");
|
| 68 |
+
|
| 69 |
+
def_pub_const!(CURSOR_API2_HOST, "api2.cursor.sh");
|
| 70 |
+
def_pub_const!(CURSOR_HOST, "www.cursor.com");
|
| 71 |
+
def_pub_const!(CURSOR_SETTINGS_URL, "https://www.cursor.com/settings");
|
| 72 |
+
|
| 73 |
+
def_pub_const!(OBJECT_CHAT_COMPLETION, "chat.completion");
|
| 74 |
+
def_pub_const!(OBJECT_CHAT_COMPLETION_CHUNK, "chat.completion.chunk");
|
| 75 |
+
|
| 76 |
+
// def_pub_const!(CURSOR_API2_STREAM_CHAT, "StreamChat");
|
| 77 |
+
// def_pub_const!(CURSOR_API2_GET_USER_INFO, "GetUserInfo");
|
| 78 |
+
|
| 79 |
+
def_pub_const!(FINISH_REASON_STOP, "stop");
|
| 80 |
+
|
| 81 |
+
def_pub_const!(ERR_INVALID_PATH, "无效的路径");
|
| 82 |
+
|
| 83 |
+
// def_pub_const!(ERR_CHECKSUM_NO_GOOD, "checksum no good");
|
src/app/lazy.rs
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use super::constant::{
|
| 2 |
+
COMMA, CURSOR_API2_HOST, CURSOR_HOST, DEFAULT_TOKEN_LIST_FILE_NAME, EMPTY_STRING,
|
| 3 |
+
};
|
| 4 |
+
use crate::common::utils::{
|
| 5 |
+
parse_ascii_char_from_env, parse_bool_from_env, parse_string_from_env, parse_usize_from_env,
|
| 6 |
+
};
|
| 7 |
+
use std::sync::LazyLock;
|
| 8 |
+
use tokio::sync::{Mutex, OnceCell};
|
| 9 |
+
|
| 10 |
+
macro_rules! def_pub_static {
|
| 11 |
+
// 基础版本:直接存储 String
|
| 12 |
+
($name:ident, $value:expr) => {
|
| 13 |
+
pub static $name: LazyLock<String> = LazyLock::new(|| $value);
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
// 环境变量版本
|
| 17 |
+
($name:ident, env: $env_key:expr, default: $default:expr) => {
|
| 18 |
+
pub static $name: LazyLock<String> =
|
| 19 |
+
LazyLock::new(|| parse_string_from_env($env_key, $default).trim().to_string());
|
| 20 |
+
};
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
// macro_rules! def_pub_static_getter {
|
| 24 |
+
// ($name:ident) => {
|
| 25 |
+
// paste::paste! {
|
| 26 |
+
// pub fn [<get_ $name:lower>]() -> String {
|
| 27 |
+
// (*$name).clone()
|
| 28 |
+
// }
|
| 29 |
+
// }
|
| 30 |
+
// };
|
| 31 |
+
// }
|
| 32 |
+
|
| 33 |
+
def_pub_static!(ROUTE_PREFIX, env: "ROUTE_PREFIX", default: EMPTY_STRING);
|
| 34 |
+
def_pub_static!(AUTH_TOKEN, env: "AUTH_TOKEN", default: EMPTY_STRING);
|
| 35 |
+
def_pub_static!(TOKEN_LIST_FILE, env: "TOKEN_LIST_FILE", default: DEFAULT_TOKEN_LIST_FILE_NAME);
|
| 36 |
+
def_pub_static!(ROUTE_MODELS_PATH, format!("{}/v1/models", *ROUTE_PREFIX));
|
| 37 |
+
def_pub_static!(
|
| 38 |
+
ROUTE_CHAT_PATH,
|
| 39 |
+
format!("{}/v1/chat/completions", *ROUTE_PREFIX)
|
| 40 |
+
);
|
| 41 |
+
|
| 42 |
+
pub static START_TIME: LazyLock<chrono::DateTime<chrono::Local>> =
|
| 43 |
+
LazyLock::new(chrono::Local::now);
|
| 44 |
+
|
| 45 |
+
pub fn get_start_time() -> chrono::DateTime<chrono::Local> {
|
| 46 |
+
*START_TIME
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
def_pub_static!(DEFAULT_INSTRUCTIONS, env: "DEFAULT_INSTRUCTIONS", default: "Respond in Chinese by default");
|
| 50 |
+
|
| 51 |
+
def_pub_static!(REVERSE_PROXY_HOST, env: "REVERSE_PROXY_HOST", default: EMPTY_STRING);
|
| 52 |
+
|
| 53 |
+
const DEFAULT_KEY_PREFIX: &str = "sk-";
|
| 54 |
+
|
| 55 |
+
pub static KEY_PREFIX: LazyLock<String> = LazyLock::new(|| {
|
| 56 |
+
let value = parse_string_from_env("KEY_PREFIX", DEFAULT_KEY_PREFIX)
|
| 57 |
+
.trim()
|
| 58 |
+
.to_string();
|
| 59 |
+
if value.is_empty() {
|
| 60 |
+
DEFAULT_KEY_PREFIX.to_string()
|
| 61 |
+
} else {
|
| 62 |
+
value
|
| 63 |
+
}
|
| 64 |
+
});
|
| 65 |
+
|
| 66 |
+
pub static KEY_PREFIX_LEN: LazyLock<usize> = LazyLock::new(|| KEY_PREFIX.len());
|
| 67 |
+
|
| 68 |
+
pub static TOKEN_DELIMITER: LazyLock<char> = LazyLock::new(|| {
|
| 69 |
+
let delimiter = parse_ascii_char_from_env("TOKEN_DELIMITER", COMMA);
|
| 70 |
+
if delimiter.is_ascii_alphabetic()
|
| 71 |
+
|| delimiter.is_ascii_digit()
|
| 72 |
+
|| delimiter == '+'
|
| 73 |
+
|| delimiter == '/'
|
| 74 |
+
{
|
| 75 |
+
COMMA
|
| 76 |
+
} else {
|
| 77 |
+
delimiter
|
| 78 |
+
}
|
| 79 |
+
});
|
| 80 |
+
|
| 81 |
+
pub static USE_COMMA_DELIMITER: LazyLock<bool> = LazyLock::new(|| {
|
| 82 |
+
let enable = parse_bool_from_env("USE_COMMA_DELIMITER", true);
|
| 83 |
+
if enable && *TOKEN_DELIMITER == COMMA {
|
| 84 |
+
false
|
| 85 |
+
} else {
|
| 86 |
+
enable
|
| 87 |
+
}
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
pub static USE_REVERSE_PROXY: LazyLock<bool> = LazyLock::new(|| !REVERSE_PROXY_HOST.is_empty());
|
| 91 |
+
|
| 92 |
+
macro_rules! def_cursor_api_url {
|
| 93 |
+
($name:ident, $api_host:expr, $path:expr) => {
|
| 94 |
+
pub static $name: LazyLock<String> = LazyLock::new(|| {
|
| 95 |
+
let host = if *USE_REVERSE_PROXY {
|
| 96 |
+
&*REVERSE_PROXY_HOST
|
| 97 |
+
} else {
|
| 98 |
+
$api_host
|
| 99 |
+
};
|
| 100 |
+
format!("https://{}{}", host, $path)
|
| 101 |
+
});
|
| 102 |
+
};
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
def_cursor_api_url!(
|
| 106 |
+
CURSOR_API2_CHAT_URL,
|
| 107 |
+
CURSOR_API2_HOST,
|
| 108 |
+
"/aiserver.v1.AiService/StreamChat"
|
| 109 |
+
);
|
| 110 |
+
|
| 111 |
+
def_cursor_api_url!(
|
| 112 |
+
CURSOR_API2_CHAT_WEB_URL,
|
| 113 |
+
CURSOR_API2_HOST,
|
| 114 |
+
"/aiserver.v1.AiService/StreamChatWeb"
|
| 115 |
+
);
|
| 116 |
+
|
| 117 |
+
def_cursor_api_url!(
|
| 118 |
+
CURSOR_API2_STRIPE_URL,
|
| 119 |
+
CURSOR_API2_HOST,
|
| 120 |
+
"/auth/full_stripe_profile"
|
| 121 |
+
);
|
| 122 |
+
|
| 123 |
+
def_cursor_api_url!(CURSOR_USAGE_API_URL, CURSOR_HOST, "/api/usage");
|
| 124 |
+
|
| 125 |
+
def_cursor_api_url!(CURSOR_USER_API_URL, CURSOR_HOST, "/api/auth/me");
|
| 126 |
+
|
| 127 |
+
pub(super) static LOGS_FILE_PATH: LazyLock<String> =
|
| 128 |
+
LazyLock::new(|| parse_string_from_env("LOGS_FILE_PATH", "logs.bin"));
|
| 129 |
+
|
| 130 |
+
pub(super) static PAGES_FILE_PATH: LazyLock<String> =
|
| 131 |
+
LazyLock::new(|| parse_string_from_env("PAGES_FILE_PATH", "pages.bin"));
|
| 132 |
+
|
| 133 |
+
pub static DEBUG: LazyLock<bool> = LazyLock::new(|| parse_bool_from_env("DEBUG", false));
|
| 134 |
+
|
| 135 |
+
// 使用环境变量 "DEBUG_LOG_FILE" 来指定日志文件路径,默认值为 "debug.log"
|
| 136 |
+
static DEBUG_LOG_FILE: LazyLock<String> =
|
| 137 |
+
LazyLock::new(|| parse_string_from_env("DEBUG_LOG_FILE", "debug.log"));
|
| 138 |
+
|
| 139 |
+
// 使用 OnceCell 结合 Mutex 来异步初始化 LOG_FILE
|
| 140 |
+
static LOG_FILE: OnceCell<Mutex<tokio::fs::File>> = OnceCell::const_new();
|
| 141 |
+
|
| 142 |
+
pub(crate) async fn get_log_file() -> &'static Mutex<tokio::fs::File> {
|
| 143 |
+
LOG_FILE
|
| 144 |
+
.get_or_init(|| async {
|
| 145 |
+
Mutex::new(
|
| 146 |
+
tokio::fs::OpenOptions::new()
|
| 147 |
+
.create(true)
|
| 148 |
+
.append(true)
|
| 149 |
+
.open(&*DEBUG_LOG_FILE)
|
| 150 |
+
.await
|
| 151 |
+
.expect("无法打开日志文件"),
|
| 152 |
+
)
|
| 153 |
+
})
|
| 154 |
+
.await
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
#[macro_export]
|
| 158 |
+
macro_rules! debug_println {
|
| 159 |
+
($($arg:tt)*) => {
|
| 160 |
+
if *crate::app::lazy::DEBUG {
|
| 161 |
+
let time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
|
| 162 |
+
let log_message = format!("{} - {}", time, format!($($arg)*));
|
| 163 |
+
use tokio::io::AsyncWriteExt as _;
|
| 164 |
+
|
| 165 |
+
// 使用 tokio 的 spawn 在后台异步写入日志
|
| 166 |
+
tokio::spawn(async move {
|
| 167 |
+
let log_file = crate::app::lazy::get_log_file().await;
|
| 168 |
+
// 使用 MutexGuard 获取可变引用
|
| 169 |
+
let mut file = log_file.lock().await;
|
| 170 |
+
if let Err(err) = file.write_all(log_message.as_bytes()).await {
|
| 171 |
+
eprintln!("写入日志文件失败: {}", err);
|
| 172 |
+
}
|
| 173 |
+
if let Err(err) = file.write_all(b"\n").await {
|
| 174 |
+
eprintln!("写入换行符失败: {}", err);
|
| 175 |
+
}
|
| 176 |
+
// 可以选择在写入失败时 panic,或者忽略
|
| 177 |
+
// panic!("写入日志文件失败: {}", err);
|
| 178 |
+
});
|
| 179 |
+
}
|
| 180 |
+
};
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
pub static REQUEST_LOGS_LIMIT: LazyLock<usize> =
|
| 184 |
+
LazyLock::new(|| std::cmp::min(parse_usize_from_env("REQUEST_LOGS_LIMIT", 100), 2000));
|
| 185 |
+
|
| 186 |
+
pub static SERVICE_TIMEOUT: LazyLock<u64> = LazyLock::new(|| {
|
| 187 |
+
let timeout = parse_usize_from_env("SERVICE_TIMEOUT", 30);
|
| 188 |
+
u64::try_from(timeout).map(|t| t.min(600)).unwrap_or(30)
|
| 189 |
+
});
|
src/app/model.rs
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::constant::{
|
| 3 |
+
EMPTY_STRING, ERR_INVALID_PATH, ROUTE_ABOUT_PATH, ROUTE_API_PATH, ROUTE_BUILD_KEY_PATH,
|
| 4 |
+
ROUTE_CONFIG_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH,
|
| 5 |
+
ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH, ROUTE_TOKENS_PATH,
|
| 6 |
+
},
|
| 7 |
+
chat::model::Message,
|
| 8 |
+
common::{
|
| 9 |
+
client::rebuild_http_client,
|
| 10 |
+
model::{userinfo::TokenProfile, ApiStatus},
|
| 11 |
+
utils::{generate_checksum_with_repair, parse_bool_from_env, parse_string_from_env},
|
| 12 |
+
},
|
| 13 |
+
};
|
| 14 |
+
use parking_lot::RwLock;
|
| 15 |
+
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
| 16 |
+
use serde::{Deserialize, Serialize};
|
| 17 |
+
use std::sync::LazyLock;
|
| 18 |
+
|
| 19 |
+
mod usage_check;
|
| 20 |
+
pub use usage_check::UsageCheck;
|
| 21 |
+
mod config;
|
| 22 |
+
mod proxies;
|
| 23 |
+
pub use proxies::Proxies;
|
| 24 |
+
mod build_key;
|
| 25 |
+
pub use build_key::*;
|
| 26 |
+
|
| 27 |
+
use super::constant::{STATUS_FAILED, STATUS_PENDING, STATUS_SUCCESS};
|
| 28 |
+
|
| 29 |
+
// 页面内容类型枚举
|
| 30 |
+
#[derive(Clone, Serialize, Deserialize, Archive, RkyvDeserialize, RkyvSerialize)]
|
| 31 |
+
#[serde(tag = "type", content = "content")]
|
| 32 |
+
pub enum PageContent {
|
| 33 |
+
#[serde(rename = "default")]
|
| 34 |
+
Default, // 默认行为
|
| 35 |
+
#[serde(rename = "text")]
|
| 36 |
+
Text(String), // 纯文本
|
| 37 |
+
#[serde(rename = "html")]
|
| 38 |
+
Html(String), // HTML 内容
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
impl Default for PageContent {
|
| 42 |
+
fn default() -> Self {
|
| 43 |
+
Self::Default
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// 静态配置
|
| 48 |
+
#[derive(Default, Clone)]
|
| 49 |
+
pub struct AppConfig {
|
| 50 |
+
vision_ability: VisionAbility,
|
| 51 |
+
slow_pool: bool,
|
| 52 |
+
allow_claude: bool,
|
| 53 |
+
pages: Pages,
|
| 54 |
+
usage_check: UsageCheck,
|
| 55 |
+
dynamic_key: bool,
|
| 56 |
+
share_token: String,
|
| 57 |
+
is_share: bool,
|
| 58 |
+
proxies: Proxies,
|
| 59 |
+
web_refs: bool,
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
|
| 63 |
+
pub enum VisionAbility {
|
| 64 |
+
#[serde(rename = "none", alias = "disabled")]
|
| 65 |
+
None,
|
| 66 |
+
#[serde(rename = "base64", alias = "base64-only")]
|
| 67 |
+
Base64,
|
| 68 |
+
#[serde(rename = "all", alias = "base64-http")]
|
| 69 |
+
All,
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
impl VisionAbility {
|
| 73 |
+
pub fn from_str(s: &str) -> Self {
|
| 74 |
+
match s.to_lowercase().as_str() {
|
| 75 |
+
"none" | "disabled" => Self::None,
|
| 76 |
+
"base64" | "base64-only" => Self::Base64,
|
| 77 |
+
"all" | "base64-http" => Self::All,
|
| 78 |
+
_ => Self::default(),
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
pub fn is_none(&self) -> bool {
|
| 83 |
+
matches!(self, VisionAbility::None)
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
impl Default for VisionAbility {
|
| 88 |
+
fn default() -> Self {
|
| 89 |
+
Self::Base64
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
#[derive(Clone, Default, Archive, RkyvDeserialize, RkyvSerialize)]
|
| 94 |
+
pub struct Pages {
|
| 95 |
+
pub root_content: PageContent,
|
| 96 |
+
pub logs_content: PageContent,
|
| 97 |
+
pub config_content: PageContent,
|
| 98 |
+
pub tokeninfo_content: PageContent,
|
| 99 |
+
pub shared_styles_content: PageContent,
|
| 100 |
+
pub shared_js_content: PageContent,
|
| 101 |
+
pub about_content: PageContent,
|
| 102 |
+
pub readme_content: PageContent,
|
| 103 |
+
pub api_content: PageContent,
|
| 104 |
+
pub build_key_content: PageContent,
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
// 运行时状态
|
| 108 |
+
pub struct AppState {
|
| 109 |
+
pub total_requests: u64,
|
| 110 |
+
pub active_requests: u64,
|
| 111 |
+
pub error_requests: u64,
|
| 112 |
+
pub request_logs: Vec<RequestLog>,
|
| 113 |
+
pub token_infos: Vec<TokenInfo>,
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
// 全局配置实例
|
| 117 |
+
pub static APP_CONFIG: LazyLock<RwLock<AppConfig>> =
|
| 118 |
+
LazyLock::new(|| RwLock::new(AppConfig::default()));
|
| 119 |
+
|
| 120 |
+
macro_rules! config_methods {
|
| 121 |
+
($($field:ident: $type:ty, $default:expr;)*) => {
|
| 122 |
+
$(
|
| 123 |
+
paste::paste! {
|
| 124 |
+
pub fn [<get_ $field>]() -> $type
|
| 125 |
+
where
|
| 126 |
+
$type: Copy + PartialEq,
|
| 127 |
+
{
|
| 128 |
+
APP_CONFIG.read().$field
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
pub fn [<update_ $field>](value: $type)
|
| 132 |
+
where
|
| 133 |
+
$type: Copy + PartialEq,
|
| 134 |
+
{
|
| 135 |
+
let current = Self::[<get_ $field>]();
|
| 136 |
+
if current != value {
|
| 137 |
+
APP_CONFIG.write().$field = value;
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
pub fn [<reset_ $field>]()
|
| 142 |
+
where
|
| 143 |
+
$type: Copy + PartialEq,
|
| 144 |
+
{
|
| 145 |
+
let default_value = $default;
|
| 146 |
+
let current = Self::[<get_ $field>]();
|
| 147 |
+
if current != default_value {
|
| 148 |
+
APP_CONFIG.write().$field = default_value;
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
)*
|
| 153 |
+
};
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
macro_rules! config_methods_clone {
|
| 157 |
+
($($field:ident: $type:ty, $default:expr;)*) => {
|
| 158 |
+
$(
|
| 159 |
+
paste::paste! {
|
| 160 |
+
pub fn [<get_ $field>]() -> $type
|
| 161 |
+
where
|
| 162 |
+
$type: Clone + PartialEq,
|
| 163 |
+
{
|
| 164 |
+
APP_CONFIG.read().$field.clone()
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
pub fn [<update_ $field>](value: $type)
|
| 168 |
+
where
|
| 169 |
+
$type: Clone + PartialEq,
|
| 170 |
+
{
|
| 171 |
+
let current = Self::[<get_ $field>]();
|
| 172 |
+
if current != value {
|
| 173 |
+
APP_CONFIG.write().$field = value;
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
pub fn [<reset_ $field>]()
|
| 178 |
+
where
|
| 179 |
+
$type: Clone + PartialEq,
|
| 180 |
+
{
|
| 181 |
+
let default_value = $default;
|
| 182 |
+
let current = Self::[<get_ $field>]();
|
| 183 |
+
if current != default_value {
|
| 184 |
+
APP_CONFIG.write().$field = default_value;
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
)*
|
| 189 |
+
};
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
impl AppConfig {
|
| 193 |
+
pub fn init() {
|
| 194 |
+
let mut config = APP_CONFIG.write();
|
| 195 |
+
config.vision_ability =
|
| 196 |
+
VisionAbility::from_str(&parse_string_from_env("VISION_ABILITY", EMPTY_STRING));
|
| 197 |
+
config.slow_pool = parse_bool_from_env("ENABLE_SLOW_POOL", false);
|
| 198 |
+
config.allow_claude = parse_bool_from_env("PASS_ANY_CLAUDE", false);
|
| 199 |
+
config.usage_check =
|
| 200 |
+
UsageCheck::from_str(&parse_string_from_env("USAGE_CHECK", EMPTY_STRING));
|
| 201 |
+
config.dynamic_key = parse_bool_from_env("DYNAMIC_KEY", false);
|
| 202 |
+
config.share_token = parse_string_from_env("SHARED_TOKEN", EMPTY_STRING);
|
| 203 |
+
config.is_share = !config.share_token.is_empty();
|
| 204 |
+
config.proxies = match std::env::var("PROXIES") {
|
| 205 |
+
Ok(proxies) => Proxies::from_str(proxies.as_str()),
|
| 206 |
+
Err(_) => Proxies::default(),
|
| 207 |
+
};
|
| 208 |
+
config.web_refs = parse_bool_from_env("INCLUDE_WEB_REFERENCES", false)
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
config_methods! {
|
| 212 |
+
slow_pool: bool, false;
|
| 213 |
+
allow_claude: bool, false;
|
| 214 |
+
dynamic_key: bool, false;
|
| 215 |
+
web_refs: bool, false;
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
config_methods_clone! {
|
| 219 |
+
vision_ability: VisionAbility, VisionAbility::default();
|
| 220 |
+
usage_check: UsageCheck, UsageCheck::default();
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
pub fn get_share_token() -> String {
|
| 224 |
+
APP_CONFIG.read().share_token.clone()
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
pub fn update_share_token(value: String) {
|
| 228 |
+
let current = Self::get_share_token();
|
| 229 |
+
if current != value {
|
| 230 |
+
let mut config = APP_CONFIG.write();
|
| 231 |
+
config.share_token = value;
|
| 232 |
+
config.is_share = !config.share_token.is_empty();
|
| 233 |
+
}
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
pub fn reset_share_token() {
|
| 237 |
+
let current = Self::get_share_token();
|
| 238 |
+
if !current.is_empty() {
|
| 239 |
+
let mut config = APP_CONFIG.write();
|
| 240 |
+
config.share_token = String::new();
|
| 241 |
+
config.is_share = false;
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
pub fn get_proxies() -> Proxies {
|
| 246 |
+
APP_CONFIG.read().proxies.clone()
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
pub fn update_proxies(value: Proxies) {
|
| 250 |
+
let current = Self::get_proxies();
|
| 251 |
+
if current != value {
|
| 252 |
+
let mut config = APP_CONFIG.write();
|
| 253 |
+
config.proxies = value;
|
| 254 |
+
rebuild_http_client();
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
pub fn reset_proxies() {
|
| 259 |
+
let default_value = Proxies::default();
|
| 260 |
+
let current = Self::get_proxies();
|
| 261 |
+
if current != default_value {
|
| 262 |
+
let mut config = APP_CONFIG.write();
|
| 263 |
+
config.proxies = default_value;
|
| 264 |
+
rebuild_http_client();
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
pub fn get_page_content(path: &str) -> Option<PageContent> {
|
| 269 |
+
match path {
|
| 270 |
+
ROUTE_ROOT_PATH => Some(APP_CONFIG.read().pages.root_content.clone()),
|
| 271 |
+
ROUTE_LOGS_PATH => Some(APP_CONFIG.read().pages.logs_content.clone()),
|
| 272 |
+
ROUTE_CONFIG_PATH => Some(APP_CONFIG.read().pages.config_content.clone()),
|
| 273 |
+
ROUTE_TOKENS_PATH => Some(APP_CONFIG.read().pages.tokeninfo_content.clone()),
|
| 274 |
+
ROUTE_SHARED_STYLES_PATH => Some(APP_CONFIG.read().pages.shared_styles_content.clone()),
|
| 275 |
+
ROUTE_SHARED_JS_PATH => Some(APP_CONFIG.read().pages.shared_js_content.clone()),
|
| 276 |
+
ROUTE_ABOUT_PATH => Some(APP_CONFIG.read().pages.about_content.clone()),
|
| 277 |
+
ROUTE_README_PATH => Some(APP_CONFIG.read().pages.readme_content.clone()),
|
| 278 |
+
ROUTE_API_PATH => Some(APP_CONFIG.read().pages.api_content.clone()),
|
| 279 |
+
ROUTE_BUILD_KEY_PATH => Some(APP_CONFIG.read().pages.build_key_content.clone()),
|
| 280 |
+
_ => None,
|
| 281 |
+
}
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
pub fn update_page_content(path: &str, content: PageContent) -> Result<(), &'static str> {
|
| 285 |
+
let mut config = APP_CONFIG.write();
|
| 286 |
+
match path {
|
| 287 |
+
ROUTE_ROOT_PATH => config.pages.root_content = content,
|
| 288 |
+
ROUTE_LOGS_PATH => config.pages.logs_content = content,
|
| 289 |
+
ROUTE_CONFIG_PATH => config.pages.config_content = content,
|
| 290 |
+
ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = content,
|
| 291 |
+
ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = content,
|
| 292 |
+
ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = content,
|
| 293 |
+
ROUTE_ABOUT_PATH => config.pages.about_content = content,
|
| 294 |
+
ROUTE_README_PATH => config.pages.readme_content = content,
|
| 295 |
+
ROUTE_API_PATH => config.pages.api_content = content,
|
| 296 |
+
ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = content,
|
| 297 |
+
_ => return Err(ERR_INVALID_PATH),
|
| 298 |
+
}
|
| 299 |
+
Ok(())
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
pub fn reset_page_content(path: &str) -> Result<(), &'static str> {
|
| 303 |
+
let mut config = APP_CONFIG.write();
|
| 304 |
+
match path {
|
| 305 |
+
ROUTE_ROOT_PATH => config.pages.root_content = PageContent::default(),
|
| 306 |
+
ROUTE_LOGS_PATH => config.pages.logs_content = PageContent::default(),
|
| 307 |
+
ROUTE_CONFIG_PATH => config.pages.config_content = PageContent::default(),
|
| 308 |
+
ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = PageContent::default(),
|
| 309 |
+
ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = PageContent::default(),
|
| 310 |
+
ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = PageContent::default(),
|
| 311 |
+
ROUTE_ABOUT_PATH => config.pages.about_content = PageContent::default(),
|
| 312 |
+
ROUTE_README_PATH => config.pages.readme_content = PageContent::default(),
|
| 313 |
+
ROUTE_API_PATH => config.pages.api_content = PageContent::default(),
|
| 314 |
+
ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = PageContent::default(),
|
| 315 |
+
_ => return Err(ERR_INVALID_PATH),
|
| 316 |
+
}
|
| 317 |
+
Ok(())
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
pub fn is_share() -> bool {
|
| 321 |
+
APP_CONFIG.read().is_share
|
| 322 |
+
}
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
impl AppState {
|
| 326 |
+
pub fn new(token_infos: Vec<TokenInfo>) -> Self {
|
| 327 |
+
// 尝试加载保存的日志
|
| 328 |
+
let request_logs = tokio::task::block_in_place(|| {
|
| 329 |
+
tokio::runtime::Handle::current()
|
| 330 |
+
.block_on(async { Self::load_saved_logs().await.unwrap_or_default() })
|
| 331 |
+
});
|
| 332 |
+
|
| 333 |
+
Self {
|
| 334 |
+
total_requests: request_logs.len() as u64,
|
| 335 |
+
active_requests: 0,
|
| 336 |
+
error_requests: request_logs
|
| 337 |
+
.iter()
|
| 338 |
+
.filter(|log| matches!(log.status, LogStatus::Failed))
|
| 339 |
+
.count() as u64,
|
| 340 |
+
request_logs,
|
| 341 |
+
token_infos,
|
| 342 |
+
}
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
pub fn update_checksum(&mut self) {
|
| 346 |
+
for token_info in self.token_infos.iter_mut() {
|
| 347 |
+
token_info.checksum = generate_checksum_with_repair(&token_info.checksum);
|
| 348 |
+
}
|
| 349 |
+
}
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
#[derive(Clone, Archive, RkyvDeserialize, RkyvSerialize)]
|
| 353 |
+
pub enum LogStatus {
|
| 354 |
+
Pending,
|
| 355 |
+
Success,
|
| 356 |
+
Failed,
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
impl Serialize for LogStatus {
|
| 360 |
+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
| 361 |
+
where
|
| 362 |
+
S: serde::Serializer,
|
| 363 |
+
{
|
| 364 |
+
serializer.serialize_str(self.as_str_name())
|
| 365 |
+
}
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
impl LogStatus {
|
| 369 |
+
pub fn as_str_name(&self) -> &'static str {
|
| 370 |
+
match self {
|
| 371 |
+
Self::Pending => STATUS_PENDING,
|
| 372 |
+
Self::Success => STATUS_SUCCESS,
|
| 373 |
+
Self::Failed => STATUS_FAILED,
|
| 374 |
+
}
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
pub fn from_str_name(s: &str) -> Option<Self> {
|
| 378 |
+
match s {
|
| 379 |
+
STATUS_PENDING => Some(Self::Pending),
|
| 380 |
+
STATUS_SUCCESS => Some(Self::Success),
|
| 381 |
+
STATUS_FAILED => Some(Self::Failed),
|
| 382 |
+
_ => None,
|
| 383 |
+
}
|
| 384 |
+
}
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
// 请求日志
|
| 388 |
+
#[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)]
|
| 389 |
+
pub struct RequestLog {
|
| 390 |
+
pub id: u64,
|
| 391 |
+
pub timestamp: chrono::DateTime<chrono::Local>,
|
| 392 |
+
pub model: String,
|
| 393 |
+
pub token_info: TokenInfo,
|
| 394 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 395 |
+
pub prompt: Option<String>,
|
| 396 |
+
pub timing: TimingInfo,
|
| 397 |
+
pub stream: bool,
|
| 398 |
+
pub status: LogStatus,
|
| 399 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 400 |
+
pub error: Option<String>,
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
#[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)]
|
| 404 |
+
pub struct TimingInfo {
|
| 405 |
+
pub total: f64, // 总用时(秒)
|
| 406 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 407 |
+
pub first: Option<f64>, // 首字时间(秒)
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
// 聊天请求
|
| 411 |
+
#[derive(Deserialize)]
|
| 412 |
+
pub struct ChatRequest {
|
| 413 |
+
pub model: String,
|
| 414 |
+
pub messages: Vec<Message>,
|
| 415 |
+
#[serde(default)]
|
| 416 |
+
pub stream: bool,
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
// 用于存储 token 信息
|
| 420 |
+
#[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)]
|
| 421 |
+
pub struct TokenInfo {
|
| 422 |
+
pub token: String,
|
| 423 |
+
pub checksum: String,
|
| 424 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 425 |
+
pub profile: Option<TokenProfile>,
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
// TokenUpdateRequest 结构体
|
| 429 |
+
#[derive(Deserialize)]
|
| 430 |
+
pub struct TokenUpdateRequest {
|
| 431 |
+
pub tokens: String,
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
#[derive(Deserialize)]
|
| 435 |
+
pub struct TokenAddRequestTokenInfo {
|
| 436 |
+
pub token: String,
|
| 437 |
+
#[serde(default)]
|
| 438 |
+
pub checksum: Option<String>,
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
// TokensDeleteRequest 结构体
|
| 442 |
+
#[derive(Deserialize)]
|
| 443 |
+
pub struct TokensDeleteRequest {
|
| 444 |
+
#[serde(default)]
|
| 445 |
+
pub tokens: Vec<String>,
|
| 446 |
+
#[serde(default)]
|
| 447 |
+
pub expectation: TokensDeleteResponseExpectation,
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
#[derive(Deserialize, Default)]
|
| 451 |
+
#[serde(rename_all = "snake_case")]
|
| 452 |
+
pub enum TokensDeleteResponseExpectation {
|
| 453 |
+
#[default]
|
| 454 |
+
Simple,
|
| 455 |
+
UpdatedTokens,
|
| 456 |
+
FailedTokens,
|
| 457 |
+
Detailed,
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
impl TokensDeleteResponseExpectation {
|
| 461 |
+
pub fn needs_updated_tokens(&self) -> bool {
|
| 462 |
+
matches!(
|
| 463 |
+
self,
|
| 464 |
+
TokensDeleteResponseExpectation::UpdatedTokens
|
| 465 |
+
| TokensDeleteResponseExpectation::Detailed
|
| 466 |
+
)
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
pub fn needs_failed_tokens(&self) -> bool {
|
| 470 |
+
matches!(
|
| 471 |
+
self,
|
| 472 |
+
TokensDeleteResponseExpectation::FailedTokens
|
| 473 |
+
| TokensDeleteResponseExpectation::Detailed
|
| 474 |
+
)
|
| 475 |
+
}
|
| 476 |
+
}
|
| 477 |
+
|
| 478 |
+
// TokensDeleteResponse 结构体
|
| 479 |
+
#[derive(Serialize)]
|
| 480 |
+
pub struct TokensDeleteResponse {
|
| 481 |
+
pub status: ApiStatus,
|
| 482 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 483 |
+
pub updated_tokens: Option<Vec<String>>,
|
| 484 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 485 |
+
pub failed_tokens: Option<Vec<String>>,
|
| 486 |
+
}
|
src/app/model/build_key.rs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use serde::{Deserialize, Serialize};
|
| 2 |
+
|
| 3 |
+
use crate::{app::constant::COMMA, chat::constant::AVAILABLE_MODELS};
|
| 4 |
+
|
| 5 |
+
#[derive(Deserialize)]
|
| 6 |
+
pub struct BuildKeyRequest {
|
| 7 |
+
pub auth_token: String,
|
| 8 |
+
#[serde(default)]
|
| 9 |
+
pub disable_vision: Option<bool>,
|
| 10 |
+
#[serde(default)]
|
| 11 |
+
pub enable_slow_pool: Option<bool>,
|
| 12 |
+
#[serde(default)]
|
| 13 |
+
pub usage_check_models: Option<UsageCheckModelConfig>,
|
| 14 |
+
#[serde(default)]
|
| 15 |
+
pub include_web_references: Option<bool>,
|
| 16 |
+
}
|
| 17 |
+
pub struct UsageCheckModelConfig {
|
| 18 |
+
pub model_type: UsageCheckModelType,
|
| 19 |
+
pub model_ids: Vec<&'static str>,
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
impl<'de> Deserialize<'de> for UsageCheckModelConfig {
|
| 23 |
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
| 24 |
+
where
|
| 25 |
+
D: serde::Deserializer<'de>,
|
| 26 |
+
{
|
| 27 |
+
#[derive(Deserialize)]
|
| 28 |
+
struct Helper {
|
| 29 |
+
#[serde(rename = "type")]
|
| 30 |
+
model_type: UsageCheckModelType,
|
| 31 |
+
#[serde(default)]
|
| 32 |
+
model_ids: String,
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
let helper = Helper::deserialize(deserializer)?;
|
| 36 |
+
|
| 37 |
+
let model_ids = if helper.model_ids.is_empty() {
|
| 38 |
+
Vec::new()
|
| 39 |
+
} else {
|
| 40 |
+
helper
|
| 41 |
+
.model_ids
|
| 42 |
+
.split(COMMA)
|
| 43 |
+
.filter_map(|model| {
|
| 44 |
+
let model = model.trim();
|
| 45 |
+
AVAILABLE_MODELS
|
| 46 |
+
.iter()
|
| 47 |
+
.find(|m| m.id == model)
|
| 48 |
+
.map(|m| m.id)
|
| 49 |
+
})
|
| 50 |
+
.collect()
|
| 51 |
+
};
|
| 52 |
+
|
| 53 |
+
Ok(UsageCheckModelConfig {
|
| 54 |
+
model_type: helper.model_type,
|
| 55 |
+
model_ids,
|
| 56 |
+
})
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
#[derive(Deserialize)]
|
| 61 |
+
#[serde(rename_all = "lowercase")]
|
| 62 |
+
pub enum UsageCheckModelType {
|
| 63 |
+
Default,
|
| 64 |
+
Disabled,
|
| 65 |
+
All,
|
| 66 |
+
Custom,
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
#[derive(Serialize)]
|
| 70 |
+
#[serde(rename_all = "lowercase")]
|
| 71 |
+
pub enum BuildKeyResponse {
|
| 72 |
+
Key(String),
|
| 73 |
+
Error(String),
|
| 74 |
+
}
|
src/app/model/config.rs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use memmap2::{MmapMut, MmapOptions};
|
| 2 |
+
use rkyv::{archived_root, Deserialize as _};
|
| 3 |
+
use std::fs::OpenOptions;
|
| 4 |
+
|
| 5 |
+
use crate::app::lazy::{LOGS_FILE_PATH, PAGES_FILE_PATH};
|
| 6 |
+
|
| 7 |
+
use super::{AppConfig, AppState, Pages, RequestLog, APP_CONFIG};
|
| 8 |
+
|
| 9 |
+
impl AppState {
|
| 10 |
+
// 保存日志的方法
|
| 11 |
+
pub(crate) async fn save_logs(&self) -> Result<(), Box<dyn std::error::Error>> {
|
| 12 |
+
// 序列化日志
|
| 13 |
+
let bytes = rkyv::to_bytes::<_, 256>(&self.request_logs)?;
|
| 14 |
+
|
| 15 |
+
// 创建或打开文件
|
| 16 |
+
let file = OpenOptions::new()
|
| 17 |
+
.read(true)
|
| 18 |
+
.write(true)
|
| 19 |
+
.create(true)
|
| 20 |
+
.open(LOGS_FILE_PATH.as_str())?;
|
| 21 |
+
|
| 22 |
+
// 添加大小检查
|
| 23 |
+
if bytes.len() > usize::MAX / 2 {
|
| 24 |
+
return Err("日志数据过大".into());
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
// 设置文件大小
|
| 28 |
+
file.set_len(bytes.len() as u64)?;
|
| 29 |
+
|
| 30 |
+
// 创建可写入的内存映射
|
| 31 |
+
let mut mmap = unsafe { MmapMut::map_mut(&file)? };
|
| 32 |
+
|
| 33 |
+
// 写入数据
|
| 34 |
+
mmap.copy_from_slice(&bytes);
|
| 35 |
+
|
| 36 |
+
// 同步到磁盘
|
| 37 |
+
mmap.flush()?;
|
| 38 |
+
|
| 39 |
+
Ok(())
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// 加载日志的方法
|
| 43 |
+
pub(super) async fn load_saved_logs() -> Result<Vec<RequestLog>, Box<dyn std::error::Error>> {
|
| 44 |
+
let file = match OpenOptions::new().read(true).open(LOGS_FILE_PATH.as_str()) {
|
| 45 |
+
Ok(file) => file,
|
| 46 |
+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
| 47 |
+
return Ok(Vec::new());
|
| 48 |
+
}
|
| 49 |
+
Err(e) => return Err(Box::new(e)),
|
| 50 |
+
};
|
| 51 |
+
|
| 52 |
+
// 添加文件大小检查
|
| 53 |
+
if file.metadata()?.len() > usize::MAX as u64 {
|
| 54 |
+
return Err("日志文件过大".into());
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
// 创建只读内存映射
|
| 58 |
+
let mmap = unsafe { MmapOptions::new().map(&file)? };
|
| 59 |
+
|
| 60 |
+
// 验证并反序列化数据
|
| 61 |
+
let archived = unsafe { archived_root::<Vec<RequestLog>>(&mmap) };
|
| 62 |
+
Ok(archived.deserialize(&mut rkyv::Infallible)?)
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
impl AppConfig {
|
| 67 |
+
pub fn save_config() -> Result<(), Box<dyn std::error::Error>> {
|
| 68 |
+
let pages = APP_CONFIG.read().pages.clone();
|
| 69 |
+
let bytes = rkyv::to_bytes::<_, 256>(&pages)?;
|
| 70 |
+
|
| 71 |
+
let file = OpenOptions::new()
|
| 72 |
+
.read(true)
|
| 73 |
+
.write(true)
|
| 74 |
+
.create(true)
|
| 75 |
+
.open(PAGES_FILE_PATH.as_str())?;
|
| 76 |
+
|
| 77 |
+
// 添加大小检查
|
| 78 |
+
if bytes.len() > usize::MAX / 2 {
|
| 79 |
+
return Err("配置数据过大".into());
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
file.set_len(bytes.len() as u64)?;
|
| 83 |
+
|
| 84 |
+
let mut mmap = unsafe { MmapMut::map_mut(&file)? };
|
| 85 |
+
mmap.copy_from_slice(&bytes);
|
| 86 |
+
mmap.flush()?;
|
| 87 |
+
|
| 88 |
+
Ok(())
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
pub fn load_saved_config() -> Result<(), Box<dyn std::error::Error>> {
|
| 92 |
+
let file = match OpenOptions::new().read(true).open(PAGES_FILE_PATH.as_str()) {
|
| 93 |
+
Ok(file) => file,
|
| 94 |
+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
| 95 |
+
return Ok(());
|
| 96 |
+
}
|
| 97 |
+
Err(e) => return Err(Box::new(e)),
|
| 98 |
+
};
|
| 99 |
+
|
| 100 |
+
// 添加文件大小检查
|
| 101 |
+
if file.metadata()?.len() > usize::MAX as u64 {
|
| 102 |
+
return Err("配置文件过大".into());
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
let mmap = unsafe { MmapOptions::new().map(&file)? };
|
| 106 |
+
|
| 107 |
+
let archived = unsafe { archived_root::<Pages>(&mmap) };
|
| 108 |
+
let pages = archived.deserialize(&mut rkyv::Infallible)?;
|
| 109 |
+
let mut config = APP_CONFIG.write();
|
| 110 |
+
config.pages = pages;
|
| 111 |
+
|
| 112 |
+
Ok(())
|
| 113 |
+
}
|
| 114 |
+
}
|
src/app/model/proxies.rs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use reqwest::{Client, Proxy};
|
| 2 |
+
use serde::{Serialize, Serializer};
|
| 3 |
+
use serde::{Deserialize, Deserializer};
|
| 4 |
+
// use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
| 5 |
+
|
| 6 |
+
use crate::app::constant::COMMA_STRING;
|
| 7 |
+
|
| 8 |
+
#[derive(Clone, Default, PartialEq)]
|
| 9 |
+
pub enum Proxies {
|
| 10 |
+
No,
|
| 11 |
+
#[default]
|
| 12 |
+
System,
|
| 13 |
+
List(Vec<String>),
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
impl Serialize for Proxies {
|
| 17 |
+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
| 18 |
+
where
|
| 19 |
+
S: Serializer,
|
| 20 |
+
{
|
| 21 |
+
match self {
|
| 22 |
+
Proxies::No => serializer.serialize_str(""),
|
| 23 |
+
Proxies::System => serializer.serialize_str("system"),
|
| 24 |
+
Proxies::List(urls) => serializer.serialize_str(&urls.join(COMMA_STRING)),
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
impl<'de> Deserialize<'de> for Proxies {
|
| 30 |
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
| 31 |
+
where
|
| 32 |
+
D: Deserializer<'de>,
|
| 33 |
+
{
|
| 34 |
+
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
|
| 35 |
+
Ok(Proxies::from_str(&s))
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
impl Proxies {
|
| 40 |
+
/// 从字符串创建 Proxies
|
| 41 |
+
///
|
| 42 |
+
/// # Arguments
|
| 43 |
+
/// * `s` - 代理字符串:
|
| 44 |
+
/// - "" 或 "no": 不使用代理
|
| 45 |
+
/// - "system": 使用系统代理
|
| 46 |
+
/// - 其他: 尝试解析为代理列表,无效则返回 System
|
| 47 |
+
pub fn from_str(s: &str) -> Self {
|
| 48 |
+
match s.trim() {
|
| 49 |
+
"" | "no" => Self::No,
|
| 50 |
+
"system" => Self::System,
|
| 51 |
+
urls => {
|
| 52 |
+
let valid_proxies: Vec<String> = urls
|
| 53 |
+
.split(',')
|
| 54 |
+
.filter_map(|url| {
|
| 55 |
+
let trimmed = url.trim();
|
| 56 |
+
(!trimmed.is_empty() && Proxy::all(trimmed).is_ok())
|
| 57 |
+
.then(|| trimmed.to_string())
|
| 58 |
+
})
|
| 59 |
+
.collect();
|
| 60 |
+
|
| 61 |
+
if valid_proxies.is_empty() {
|
| 62 |
+
Self::default()
|
| 63 |
+
} else {
|
| 64 |
+
Self::List(valid_proxies)
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
pub fn get_client(&self) -> Client {
|
| 71 |
+
match self {
|
| 72 |
+
Proxies::No => Client::builder().no_proxy().build().unwrap(),
|
| 73 |
+
Proxies::System => Client::new(),
|
| 74 |
+
Proxies::List(list) => {
|
| 75 |
+
// 使用第一个代理(已经确保是有效的)
|
| 76 |
+
let proxy = Proxy::all(list[0].clone()).unwrap();
|
| 77 |
+
Client::builder().proxy(proxy).build().unwrap()
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
}
|
src/app/model/usage_check.rs
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::constant::{COMMA, COMMA_STRING},
|
| 3 |
+
chat::{config::key_config, constant::AVAILABLE_MODELS},
|
| 4 |
+
};
|
| 5 |
+
use serde::{Deserialize, Serialize};
|
| 6 |
+
// use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
| 7 |
+
|
| 8 |
+
#[derive(Clone, PartialEq)]
|
| 9 |
+
pub enum UsageCheck {
|
| 10 |
+
None,
|
| 11 |
+
Default,
|
| 12 |
+
All,
|
| 13 |
+
Custom(Vec<&'static str>),
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
impl UsageCheck {
|
| 17 |
+
pub fn from_proto(model: Option<&key_config::UsageCheckModel>) -> Option<Self> {
|
| 18 |
+
model.map(|model| {
|
| 19 |
+
use key_config::usage_check_model::Type;
|
| 20 |
+
match Type::try_from(model.r#type).unwrap_or(Type::Default) {
|
| 21 |
+
Type::Default | Type::Disabled => Self::None,
|
| 22 |
+
Type::All => Self::All,
|
| 23 |
+
Type::Custom => {
|
| 24 |
+
let models: Vec<&'static str> = model
|
| 25 |
+
.model_ids
|
| 26 |
+
.iter()
|
| 27 |
+
.filter_map(|id| AVAILABLE_MODELS.iter().find(|m| m.id == id).map(|m| m.id))
|
| 28 |
+
.collect();
|
| 29 |
+
if models.is_empty() {
|
| 30 |
+
Self::None
|
| 31 |
+
} else {
|
| 32 |
+
Self::Custom(models)
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
})
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
// pub fn to_proto(&self) -> key_config::UsageCheckModel {
|
| 40 |
+
// use key_config::usage_check_model::Type;
|
| 41 |
+
// match self {
|
| 42 |
+
// Self::None => key_config::UsageCheckModel {
|
| 43 |
+
// r#type: Type::Disabled.into(),
|
| 44 |
+
// model_ids: vec![],
|
| 45 |
+
// },
|
| 46 |
+
// Self::Default => key_config::UsageCheckModel {
|
| 47 |
+
// r#type: Type::Default.into(),
|
| 48 |
+
// model_ids: vec![],
|
| 49 |
+
// },
|
| 50 |
+
// Self::All => key_config::UsageCheckModel {
|
| 51 |
+
// r#type: Type::All.into(),
|
| 52 |
+
// model_ids: vec![],
|
| 53 |
+
// },
|
| 54 |
+
// Self::Custom(models) => key_config::UsageCheckModel {
|
| 55 |
+
// r#type: Type::Custom.into(),
|
| 56 |
+
// model_ids: models.iter().map(|&s| s.to_string()).collect(),
|
| 57 |
+
// },
|
| 58 |
+
// }
|
| 59 |
+
// }
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
impl Default for UsageCheck {
|
| 63 |
+
fn default() -> Self {
|
| 64 |
+
Self::Default
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
impl Serialize for UsageCheck {
|
| 69 |
+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
| 70 |
+
where
|
| 71 |
+
S: serde::Serializer,
|
| 72 |
+
{
|
| 73 |
+
use serde::ser::SerializeStruct;
|
| 74 |
+
let mut state = serializer.serialize_struct("UsageCheck", 1)?;
|
| 75 |
+
match self {
|
| 76 |
+
UsageCheck::None => {
|
| 77 |
+
state.serialize_field("type", "none")?;
|
| 78 |
+
}
|
| 79 |
+
UsageCheck::Default => {
|
| 80 |
+
state.serialize_field("type", "default")?;
|
| 81 |
+
}
|
| 82 |
+
UsageCheck::All => {
|
| 83 |
+
state.serialize_field("type", "all")?;
|
| 84 |
+
}
|
| 85 |
+
UsageCheck::Custom(models) => {
|
| 86 |
+
state.serialize_field("type", "list")?;
|
| 87 |
+
state.serialize_field("content", &models.join(COMMA_STRING))?;
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
state.end()
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
impl<'de> Deserialize<'de> for UsageCheck {
|
| 95 |
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
| 96 |
+
where
|
| 97 |
+
D: serde::Deserializer<'de>,
|
| 98 |
+
{
|
| 99 |
+
#[derive(Deserialize)]
|
| 100 |
+
#[serde(tag = "type", content = "content")]
|
| 101 |
+
enum UsageCheckHelper {
|
| 102 |
+
#[serde(rename = "none")]
|
| 103 |
+
None,
|
| 104 |
+
#[serde(rename = "default")]
|
| 105 |
+
Default,
|
| 106 |
+
#[serde(rename = "all")]
|
| 107 |
+
All,
|
| 108 |
+
#[serde(rename = "list")]
|
| 109 |
+
Custom(String),
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
let helper = <UsageCheckHelper as serde::Deserialize>::deserialize(deserializer)?;
|
| 113 |
+
Ok(match helper {
|
| 114 |
+
UsageCheckHelper::None => UsageCheck::None,
|
| 115 |
+
UsageCheckHelper::Default => UsageCheck::Default,
|
| 116 |
+
UsageCheckHelper::All => UsageCheck::All,
|
| 117 |
+
UsageCheckHelper::Custom(list) => {
|
| 118 |
+
if list.is_empty() {
|
| 119 |
+
return Ok(UsageCheck::None);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
let models: Vec<&'static str> = list
|
| 123 |
+
.split(COMMA)
|
| 124 |
+
.filter_map(|model| {
|
| 125 |
+
let model = model.trim();
|
| 126 |
+
AVAILABLE_MODELS
|
| 127 |
+
.iter()
|
| 128 |
+
.find(|m| m.id == model)
|
| 129 |
+
.map(|m| m.id)
|
| 130 |
+
})
|
| 131 |
+
.collect();
|
| 132 |
+
|
| 133 |
+
if models.is_empty() {
|
| 134 |
+
UsageCheck::None
|
| 135 |
+
} else {
|
| 136 |
+
UsageCheck::Custom(models)
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
})
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
impl UsageCheck {
|
| 144 |
+
pub fn from_str(s: &str) -> Self {
|
| 145 |
+
match s.trim().to_lowercase().as_str() {
|
| 146 |
+
"none" | "disabled" => Self::None,
|
| 147 |
+
"default" => Self::Default,
|
| 148 |
+
"all" | "everything" => Self::All,
|
| 149 |
+
list => {
|
| 150 |
+
if list.is_empty() {
|
| 151 |
+
return Self::default();
|
| 152 |
+
}
|
| 153 |
+
let models: Vec<&'static str> = list
|
| 154 |
+
.split(COMMA)
|
| 155 |
+
.filter_map(|model| {
|
| 156 |
+
let model = model.trim();
|
| 157 |
+
AVAILABLE_MODELS
|
| 158 |
+
.iter()
|
| 159 |
+
.find(|m| m.id == model)
|
| 160 |
+
.map(|m| m.id)
|
| 161 |
+
})
|
| 162 |
+
.collect();
|
| 163 |
+
|
| 164 |
+
if models.is_empty() {
|
| 165 |
+
Self::default()
|
| 166 |
+
} else {
|
| 167 |
+
Self::Custom(models)
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
}
|
| 172 |
+
}
|
src/chat.rs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pub mod adapter;
|
| 2 |
+
pub mod aiserver;
|
| 3 |
+
pub mod config;
|
| 4 |
+
pub mod constant;
|
| 5 |
+
pub mod error;
|
| 6 |
+
// pub mod middleware;
|
| 7 |
+
pub mod model;
|
| 8 |
+
pub mod route;
|
| 9 |
+
pub mod service;
|
| 10 |
+
pub mod stream;
|
src/chat/adapter.rs
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
|
| 2 |
+
use image::guess_format;
|
| 3 |
+
use prost::Message as _;
|
| 4 |
+
use reqwest::Client;
|
| 5 |
+
use uuid::Uuid;
|
| 6 |
+
|
| 7 |
+
use crate::{
|
| 8 |
+
app::{
|
| 9 |
+
constant::EMPTY_STRING,
|
| 10 |
+
lazy::DEFAULT_INSTRUCTIONS,
|
| 11 |
+
model::{AppConfig, VisionAbility},
|
| 12 |
+
},
|
| 13 |
+
common::client::HTTP_CLIENT,
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
use super::{
|
| 17 |
+
aiserver::v1::{
|
| 18 |
+
conversation_message, image_proto, AzureState, ChatExternalLink, ConversationMessage, ExplicitContext, GetChatRequest, ImageProto, ModelDetails
|
| 19 |
+
},
|
| 20 |
+
constant::{ERR_UNSUPPORTED_GIF, ERR_UNSUPPORTED_IMAGE_FORMAT, LONG_CONTEXT_MODELS},
|
| 21 |
+
model::{Message, MessageContent, Role},
|
| 22 |
+
};
|
| 23 |
+
|
| 24 |
+
async fn process_chat_inputs(
|
| 25 |
+
inputs: Vec<Message>,
|
| 26 |
+
disable_vision: bool,
|
| 27 |
+
) -> (String, Vec<ConversationMessage>, Vec<String>) {
|
| 28 |
+
// 收集 system 指令
|
| 29 |
+
let instructions = inputs
|
| 30 |
+
.iter()
|
| 31 |
+
.filter(|input| input.role == Role::System)
|
| 32 |
+
.map(|input| match &input.content {
|
| 33 |
+
MessageContent::Text(text) => text.clone(),
|
| 34 |
+
MessageContent::Vision(contents) => contents
|
| 35 |
+
.iter()
|
| 36 |
+
.filter_map(|content| {
|
| 37 |
+
if content.content_type == "text" {
|
| 38 |
+
content.text.clone()
|
| 39 |
+
} else {
|
| 40 |
+
None
|
| 41 |
+
}
|
| 42 |
+
})
|
| 43 |
+
.collect::<Vec<String>>()
|
| 44 |
+
.join("\n"),
|
| 45 |
+
})
|
| 46 |
+
.collect::<Vec<String>>()
|
| 47 |
+
.join("\n\n");
|
| 48 |
+
|
| 49 |
+
// 使用默认指令或收集到的指令
|
| 50 |
+
let instructions = if instructions.is_empty() {
|
| 51 |
+
DEFAULT_INSTRUCTIONS.clone()
|
| 52 |
+
} else {
|
| 53 |
+
instructions
|
| 54 |
+
};
|
| 55 |
+
|
| 56 |
+
// 过滤出 user 和 assistant 对话
|
| 57 |
+
let mut chat_inputs: Vec<Message> = inputs
|
| 58 |
+
.into_iter()
|
| 59 |
+
.filter(|input| input.role == Role::User || input.role == Role::Assistant)
|
| 60 |
+
.collect();
|
| 61 |
+
|
| 62 |
+
// 处理空对话情况
|
| 63 |
+
if chat_inputs.is_empty() {
|
| 64 |
+
return (
|
| 65 |
+
instructions,
|
| 66 |
+
vec![ConversationMessage {
|
| 67 |
+
text: EMPTY_STRING.into(),
|
| 68 |
+
r#type: conversation_message::MessageType::Human as i32,
|
| 69 |
+
attached_code_chunks: vec![],
|
| 70 |
+
codebase_context_chunks: vec![],
|
| 71 |
+
commits: vec![],
|
| 72 |
+
pull_requests: vec![],
|
| 73 |
+
git_diffs: vec![],
|
| 74 |
+
assistant_suggested_diffs: vec![],
|
| 75 |
+
interpreter_results: vec![],
|
| 76 |
+
images: vec![],
|
| 77 |
+
attached_folders: vec![],
|
| 78 |
+
approximate_lint_errors: vec![],
|
| 79 |
+
bubble_id: Uuid::new_v4().to_string(),
|
| 80 |
+
server_bubble_id: None,
|
| 81 |
+
attached_folders_new: vec![],
|
| 82 |
+
lints: vec![],
|
| 83 |
+
user_responses_to_suggested_code_blocks: vec![],
|
| 84 |
+
relevant_files: vec![],
|
| 85 |
+
tool_results: vec![],
|
| 86 |
+
notepads: vec![],
|
| 87 |
+
is_capability_iteration: Some(false),
|
| 88 |
+
capabilities: vec![],
|
| 89 |
+
edit_trail_contexts: vec![],
|
| 90 |
+
suggested_code_blocks: vec![],
|
| 91 |
+
diffs_for_compressing_files: vec![],
|
| 92 |
+
multi_file_linter_errors: vec![],
|
| 93 |
+
diff_histories: vec![],
|
| 94 |
+
recently_viewed_files: vec![],
|
| 95 |
+
recent_locations_history: vec![],
|
| 96 |
+
is_agentic: false,
|
| 97 |
+
file_diff_trajectories: vec![],
|
| 98 |
+
conversation_summary: None,
|
| 99 |
+
}],
|
| 100 |
+
vec![],
|
| 101 |
+
);
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
// 处理 WebReferences 开头的 assistant 消息
|
| 105 |
+
chat_inputs = chat_inputs
|
| 106 |
+
.into_iter()
|
| 107 |
+
.map(|mut input| {
|
| 108 |
+
if let (Role::Assistant, MessageContent::Text(text)) = (&input.role, &input.content) {
|
| 109 |
+
if text.starts_with("WebReferences:") {
|
| 110 |
+
if let Some(pos) = text.find("\n\n") {
|
| 111 |
+
input.content = MessageContent::Text(text[pos + 2..].to_owned());
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
input
|
| 116 |
+
})
|
| 117 |
+
.collect();
|
| 118 |
+
|
| 119 |
+
// 如果第一条是 assistant,插入空的 user 消息
|
| 120 |
+
if chat_inputs
|
| 121 |
+
.first()
|
| 122 |
+
.map_or(false, |input| input.role == Role::Assistant)
|
| 123 |
+
{
|
| 124 |
+
chat_inputs.insert(
|
| 125 |
+
0,
|
| 126 |
+
Message {
|
| 127 |
+
role: Role::User,
|
| 128 |
+
content: MessageContent::Text(EMPTY_STRING.into()),
|
| 129 |
+
},
|
| 130 |
+
);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
// 处理连续相同角色的情况
|
| 134 |
+
let mut i = 1;
|
| 135 |
+
while i < chat_inputs.len() {
|
| 136 |
+
if chat_inputs[i].role == chat_inputs[i - 1].role {
|
| 137 |
+
let insert_role = if chat_inputs[i].role == Role::User {
|
| 138 |
+
Role::Assistant
|
| 139 |
+
} else {
|
| 140 |
+
Role::User
|
| 141 |
+
};
|
| 142 |
+
chat_inputs.insert(
|
| 143 |
+
i,
|
| 144 |
+
Message {
|
| 145 |
+
role: insert_role,
|
| 146 |
+
content: MessageContent::Text(EMPTY_STRING.into()),
|
| 147 |
+
},
|
| 148 |
+
);
|
| 149 |
+
}
|
| 150 |
+
i += 1;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
// 确保最后一条是 user
|
| 154 |
+
if chat_inputs
|
| 155 |
+
.last()
|
| 156 |
+
.map_or(false, |input| input.role == Role::Assistant)
|
| 157 |
+
{
|
| 158 |
+
chat_inputs.push(Message {
|
| 159 |
+
role: Role::User,
|
| 160 |
+
content: MessageContent::Text(EMPTY_STRING.into()),
|
| 161 |
+
});
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
// 转换为 proto messages
|
| 165 |
+
let mut messages = Vec::new();
|
| 166 |
+
for input in chat_inputs {
|
| 167 |
+
let (text, images) = match input.content {
|
| 168 |
+
MessageContent::Text(text) => (text, vec![]),
|
| 169 |
+
MessageContent::Vision(contents) => {
|
| 170 |
+
let mut text_parts = Vec::new();
|
| 171 |
+
let mut images = Vec::new();
|
| 172 |
+
|
| 173 |
+
for content in contents {
|
| 174 |
+
match content.content_type.as_str() {
|
| 175 |
+
"text" => {
|
| 176 |
+
if let Some(text) = content.text {
|
| 177 |
+
text_parts.push(text);
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
"image_url" => {
|
| 181 |
+
if !disable_vision {
|
| 182 |
+
if let Some(image_url) = &content.image_url {
|
| 183 |
+
let url = image_url.url.clone();
|
| 184 |
+
let client = HTTP_CLIENT.read().clone();
|
| 185 |
+
let result = tokio::spawn(async move {
|
| 186 |
+
fetch_image_data(&url, client).await
|
| 187 |
+
});
|
| 188 |
+
if let Ok(Ok((image_data, dimensions))) = result.await {
|
| 189 |
+
images.push(ImageProto {
|
| 190 |
+
data: image_data,
|
| 191 |
+
dimension: dimensions,
|
| 192 |
+
});
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
_ => {}
|
| 198 |
+
}
|
| 199 |
+
}
|
| 200 |
+
(text_parts.join("\n"), images)
|
| 201 |
+
}
|
| 202 |
+
};
|
| 203 |
+
|
| 204 |
+
messages.push(ConversationMessage {
|
| 205 |
+
text,
|
| 206 |
+
r#type: if input.role == Role::User {
|
| 207 |
+
conversation_message::MessageType::Human as i32
|
| 208 |
+
} else {
|
| 209 |
+
conversation_message::MessageType::Ai as i32
|
| 210 |
+
},
|
| 211 |
+
attached_code_chunks: vec![],
|
| 212 |
+
codebase_context_chunks: vec![],
|
| 213 |
+
commits: vec![],
|
| 214 |
+
pull_requests: vec![],
|
| 215 |
+
git_diffs: vec![],
|
| 216 |
+
assistant_suggested_diffs: vec![],
|
| 217 |
+
interpreter_results: vec![],
|
| 218 |
+
images,
|
| 219 |
+
attached_folders: vec![],
|
| 220 |
+
approximate_lint_errors: vec![],
|
| 221 |
+
bubble_id: Uuid::new_v4().to_string(),
|
| 222 |
+
server_bubble_id: None,
|
| 223 |
+
attached_folders_new: vec![],
|
| 224 |
+
lints: vec![],
|
| 225 |
+
user_responses_to_suggested_code_blocks: vec![],
|
| 226 |
+
relevant_files: vec![],
|
| 227 |
+
tool_results: vec![],
|
| 228 |
+
notepads: vec![],
|
| 229 |
+
is_capability_iteration: None,
|
| 230 |
+
capabilities: vec![],
|
| 231 |
+
edit_trail_contexts: vec![],
|
| 232 |
+
suggested_code_blocks: vec![],
|
| 233 |
+
diffs_for_compressing_files: vec![],
|
| 234 |
+
multi_file_linter_errors: vec![],
|
| 235 |
+
diff_histories: vec![],
|
| 236 |
+
recently_viewed_files: vec![],
|
| 237 |
+
recent_locations_history: vec![],
|
| 238 |
+
is_agentic: false,
|
| 239 |
+
file_diff_trajectories: vec![],
|
| 240 |
+
conversation_summary: None,
|
| 241 |
+
});
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
let mut urls = Vec::new();
|
| 245 |
+
if let Some(last_msg) = messages.last() {
|
| 246 |
+
if last_msg.r#type == conversation_message::MessageType::Human as i32 {
|
| 247 |
+
let text = &last_msg.text;
|
| 248 |
+
let mut chars = text.chars().peekable();
|
| 249 |
+
|
| 250 |
+
while let Some(c) = chars.next() {
|
| 251 |
+
if c == '@' {
|
| 252 |
+
let mut url = String::new();
|
| 253 |
+
while let Some(&next_char) = chars.peek() {
|
| 254 |
+
if next_char.is_whitespace() {
|
| 255 |
+
break;
|
| 256 |
+
}
|
| 257 |
+
url.push(chars.next().unwrap());
|
| 258 |
+
}
|
| 259 |
+
if let Ok(parsed_url) = url::Url::parse(&url) {
|
| 260 |
+
if parsed_url.scheme() == "http" || parsed_url.scheme() == "https" {
|
| 261 |
+
urls.push(url);
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
(instructions, messages, urls)
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
async fn fetch_image_data(
|
| 273 |
+
url: &str,
|
| 274 |
+
client: Client,
|
| 275 |
+
) -> Result<(Vec<u8>, Option<image_proto::Dimension>), Box<dyn std::error::Error + Send + Sync>> {
|
| 276 |
+
// 在进入异步操作前获取并释放锁
|
| 277 |
+
let vision_ability = AppConfig::get_vision_ability();
|
| 278 |
+
|
| 279 |
+
match vision_ability {
|
| 280 |
+
VisionAbility::None => Err("图片功能已禁用".into()),
|
| 281 |
+
|
| 282 |
+
VisionAbility::Base64 => {
|
| 283 |
+
if !url.starts_with("data:image/") {
|
| 284 |
+
return Err("仅支持 base64 编码的图片".into());
|
| 285 |
+
}
|
| 286 |
+
process_base64_image(url)
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
VisionAbility::All => {
|
| 290 |
+
if url.starts_with("data:image/") {
|
| 291 |
+
process_base64_image(url)
|
| 292 |
+
} else {
|
| 293 |
+
process_http_image(url, client).await
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
}
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
// 处理 base64 编码的图片
|
| 300 |
+
fn process_base64_image(
|
| 301 |
+
url: &str,
|
| 302 |
+
) -> Result<(Vec<u8>, Option<image_proto::Dimension>), Box<dyn std::error::Error + Send + Sync>> {
|
| 303 |
+
let parts: Vec<&str> = url.split("base64,").collect();
|
| 304 |
+
if parts.len() != 2 {
|
| 305 |
+
return Err("无效的 base64 图片格式".into());
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
// 检查图片格式
|
| 309 |
+
let format = parts[0].to_lowercase();
|
| 310 |
+
if !format.contains("png")
|
| 311 |
+
&& !format.contains("jpeg")
|
| 312 |
+
&& !format.contains("jpg")
|
| 313 |
+
&& !format.contains("webp")
|
| 314 |
+
&& !format.contains("gif")
|
| 315 |
+
{
|
| 316 |
+
return Err(ERR_UNSUPPORTED_IMAGE_FORMAT.into());
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
let image_data = BASE64.decode(parts[1])?;
|
| 320 |
+
|
| 321 |
+
// 检查是否为动态 GIF
|
| 322 |
+
if format.contains("gif") {
|
| 323 |
+
if let Ok(frames) = gif::DecodeOptions::new().read_info(std::io::Cursor::new(&image_data)) {
|
| 324 |
+
if frames.into_iter().count() > 1 {
|
| 325 |
+
return Err(ERR_UNSUPPORTED_GIF.into());
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
// 获取图片尺寸
|
| 331 |
+
let dimensions = if let Ok(img) = image::load_from_memory(&image_data) {
|
| 332 |
+
Some(image_proto::Dimension {
|
| 333 |
+
width: img.width() as i32,
|
| 334 |
+
height: img.height() as i32,
|
| 335 |
+
})
|
| 336 |
+
} else {
|
| 337 |
+
None
|
| 338 |
+
};
|
| 339 |
+
|
| 340 |
+
Ok((image_data, dimensions))
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
// 处理 HTTP 图片 URL
|
| 344 |
+
async fn process_http_image(
|
| 345 |
+
url: &str,
|
| 346 |
+
client: Client,
|
| 347 |
+
) -> Result<(Vec<u8>, Option<image_proto::Dimension>), Box<dyn std::error::Error + Send + Sync>> {
|
| 348 |
+
let response = client.get(url).send().await?;
|
| 349 |
+
let image_data = response.bytes().await?.to_vec();
|
| 350 |
+
let format = guess_format(&image_data)?;
|
| 351 |
+
|
| 352 |
+
// 检查图片格式
|
| 353 |
+
match format {
|
| 354 |
+
image::ImageFormat::Png | image::ImageFormat::Jpeg | image::ImageFormat::WebP => {
|
| 355 |
+
// 这些格式都支持
|
| 356 |
+
}
|
| 357 |
+
image::ImageFormat::Gif => {
|
| 358 |
+
if let Ok(frames) =
|
| 359 |
+
gif::DecodeOptions::new().read_info(std::io::Cursor::new(&image_data))
|
| 360 |
+
{
|
| 361 |
+
if frames.into_iter().count() > 1 {
|
| 362 |
+
return Err(ERR_UNSUPPORTED_GIF.into());
|
| 363 |
+
}
|
| 364 |
+
}
|
| 365 |
+
}
|
| 366 |
+
_ => return Err(ERR_UNSUPPORTED_IMAGE_FORMAT.into()),
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
// 获取图片尺寸
|
| 370 |
+
let dimensions = if let Ok(img) = image::load_from_memory_with_format(&image_data, format) {
|
| 371 |
+
Some(image_proto::Dimension {
|
| 372 |
+
width: img.width() as i32,
|
| 373 |
+
height: img.height() as i32,
|
| 374 |
+
})
|
| 375 |
+
} else {
|
| 376 |
+
None
|
| 377 |
+
};
|
| 378 |
+
|
| 379 |
+
Ok((image_data, dimensions))
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
pub async fn encode_chat_message(
|
| 383 |
+
inputs: Vec<Message>,
|
| 384 |
+
model_name: &str,
|
| 385 |
+
disable_vision: bool,
|
| 386 |
+
enable_slow_pool: bool,
|
| 387 |
+
is_search: bool,
|
| 388 |
+
) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
| 389 |
+
// 在进入异步操作前获取并释放锁
|
| 390 |
+
let enable_slow_pool = {
|
| 391 |
+
if enable_slow_pool {
|
| 392 |
+
Some(true)
|
| 393 |
+
} else {
|
| 394 |
+
None
|
| 395 |
+
}
|
| 396 |
+
};
|
| 397 |
+
|
| 398 |
+
let (instructions, messages, urls) = process_chat_inputs(inputs, disable_vision).await;
|
| 399 |
+
|
| 400 |
+
let explicit_context = if !instructions.trim().is_empty() {
|
| 401 |
+
Some(ExplicitContext {
|
| 402 |
+
context: instructions,
|
| 403 |
+
repo_context: None,
|
| 404 |
+
})
|
| 405 |
+
} else {
|
| 406 |
+
None
|
| 407 |
+
};
|
| 408 |
+
|
| 409 |
+
let base_uuid = rand::random::<u16>();
|
| 410 |
+
let external_links = urls.into_iter().enumerate().map(|(i, url)| {
|
| 411 |
+
let uuid = base_uuid.wrapping_add(i as u16);
|
| 412 |
+
ChatExternalLink {
|
| 413 |
+
url,
|
| 414 |
+
uuid: uuid.to_string(),
|
| 415 |
+
}
|
| 416 |
+
}).collect();
|
| 417 |
+
|
| 418 |
+
let chat = GetChatRequest {
|
| 419 |
+
current_file: None,
|
| 420 |
+
conversation: messages,
|
| 421 |
+
repositories: vec![],
|
| 422 |
+
explicit_context,
|
| 423 |
+
workspace_root_path: None,
|
| 424 |
+
code_blocks: vec![],
|
| 425 |
+
model_details: Some(ModelDetails {
|
| 426 |
+
model_name: Some(model_name.to_string()),
|
| 427 |
+
api_key: None,
|
| 428 |
+
enable_ghost_mode: None,
|
| 429 |
+
azure_state: Some(AzureState {
|
| 430 |
+
api_key: String::new(),
|
| 431 |
+
base_url: String::new(),
|
| 432 |
+
deployment: String::new(),
|
| 433 |
+
use_azure: false,
|
| 434 |
+
}),
|
| 435 |
+
enable_slow_pool,
|
| 436 |
+
openai_api_base_url: None,
|
| 437 |
+
}),
|
| 438 |
+
documentation_identifiers: vec![],
|
| 439 |
+
request_id: Uuid::new_v4().to_string(),
|
| 440 |
+
linter_errors: None,
|
| 441 |
+
summary: None,
|
| 442 |
+
summary_up_until_index: None,
|
| 443 |
+
allow_long_file_scan: Some(false),
|
| 444 |
+
is_bash: Some(false),
|
| 445 |
+
conversation_id: Uuid::new_v4().to_string(),
|
| 446 |
+
can_handle_filenames_after_language_ids: Some(true),
|
| 447 |
+
use_web: if is_search {
|
| 448 |
+
Some("full_search".to_string())
|
| 449 |
+
} else {
|
| 450 |
+
None
|
| 451 |
+
},
|
| 452 |
+
quotes: vec![],
|
| 453 |
+
debug_info: None,
|
| 454 |
+
workspace_id: None,
|
| 455 |
+
external_links,
|
| 456 |
+
commit_notes: vec![],
|
| 457 |
+
long_context_mode: Some(LONG_CONTEXT_MODELS.contains(&model_name)),
|
| 458 |
+
is_eval: Some(false),
|
| 459 |
+
desired_max_tokens: None,
|
| 460 |
+
context_ast: None,
|
| 461 |
+
is_composer: None,
|
| 462 |
+
runnable_code_blocks: Some(false),
|
| 463 |
+
should_cache: Some(false),
|
| 464 |
+
};
|
| 465 |
+
|
| 466 |
+
let mut encoded = Vec::new();
|
| 467 |
+
chat.encode(&mut encoded)?;
|
| 468 |
+
|
| 469 |
+
let len_prefix = format!("{:010x}", encoded.len()).to_uppercase();
|
| 470 |
+
let content = hex::encode_upper(&encoded);
|
| 471 |
+
|
| 472 |
+
Ok(hex::decode(len_prefix + &content)?)
|
| 473 |
+
}
|
src/chat/aiserver.rs
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pub mod v1;
|
src/chat/aiserver/v1.rs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
include!(concat!(env!("OUT_DIR"), "/aiserver.v1.rs"));
|
| 2 |
+
use error_details::Error;
|
| 3 |
+
|
| 4 |
+
impl ErrorDetails {
|
| 5 |
+
pub fn status_code(&self) -> u16 {
|
| 6 |
+
match Error::try_from(self.error) {
|
| 7 |
+
Ok(error) => match error {
|
| 8 |
+
Error::Unspecified => 500,
|
| 9 |
+
Error::BadApiKey
|
| 10 |
+
| Error::InvalidAuthId
|
| 11 |
+
| Error::AuthTokenNotFound
|
| 12 |
+
| Error::AuthTokenExpired
|
| 13 |
+
| Error::Unauthorized => 401,
|
| 14 |
+
Error::NotLoggedIn
|
| 15 |
+
| Error::NotHighEnoughPermissions
|
| 16 |
+
| Error::AgentRequiresLogin
|
| 17 |
+
| Error::ProUserOnly
|
| 18 |
+
| Error::TaskNoPermissions => 403,
|
| 19 |
+
Error::NotFound
|
| 20 |
+
| Error::UserNotFound
|
| 21 |
+
| Error::TaskUuidNotFound
|
| 22 |
+
| Error::AgentEngineNotFound
|
| 23 |
+
| Error::GitgraphNotFound
|
| 24 |
+
| Error::FileNotFound => 404,
|
| 25 |
+
Error::FreeUserRateLimitExceeded
|
| 26 |
+
| Error::ProUserRateLimitExceeded
|
| 27 |
+
| Error::OpenaiRateLimitExceeded
|
| 28 |
+
| Error::OpenaiAccountLimitExceeded
|
| 29 |
+
| Error::GenericRateLimitExceeded
|
| 30 |
+
| Error::Gpt4VisionPreviewRateLimit
|
| 31 |
+
| Error::ApiKeyRateLimit => 429,
|
| 32 |
+
Error::BadRequest
|
| 33 |
+
| Error::BadModelName
|
| 34 |
+
| Error::SlashEditFileTooLong
|
| 35 |
+
| Error::FileUnsupported
|
| 36 |
+
| Error::ClaudeImageTooLarge => 400,
|
| 37 |
+
Error::Deprecated
|
| 38 |
+
| Error::FreeUserUsageLimit
|
| 39 |
+
| Error::ProUserUsageLimit
|
| 40 |
+
| Error::ResourceExhausted
|
| 41 |
+
| Error::Openai
|
| 42 |
+
| Error::MaxTokens
|
| 43 |
+
| Error::ApiKeyNotSupported
|
| 44 |
+
| Error::UserAbortedRequest
|
| 45 |
+
| Error::CustomMessage
|
| 46 |
+
| Error::OutdatedClient
|
| 47 |
+
| Error::Debounced
|
| 48 |
+
| Error::RepositoryServiceRepositoryIsNotInitialized => 500,
|
| 49 |
+
},
|
| 50 |
+
Err(_) => 500,
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
// pub fn is_expected(&self) -> bool {
|
| 55 |
+
// self.is_expected.unwrap_or_default()
|
| 56 |
+
// }
|
| 57 |
+
}
|
src/chat/aiserver/v1/lite.proto
ADDED
|
@@ -0,0 +1,1156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
syntax = "proto3";
|
| 2 |
+
package aiserver.v1;
|
| 3 |
+
enum ClientSideToolV2 { // aiserver.v1.ClientSideToolV2
|
| 4 |
+
CLIENT_SIDE_TOOL_V2_UNSPECIFIED = 0;
|
| 5 |
+
CLIENT_SIDE_TOOL_V2_READ_SEMSEARCH_FILES = 1;
|
| 6 |
+
CLIENT_SIDE_TOOL_V2_READ_FILE_FOR_IMPORTS = 2;
|
| 7 |
+
CLIENT_SIDE_TOOL_V2_RIPGREP_SEARCH = 3;
|
| 8 |
+
CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND = 4;
|
| 9 |
+
CLIENT_SIDE_TOOL_V2_READ_FILE = 5;
|
| 10 |
+
CLIENT_SIDE_TOOL_V2_LIST_DIR = 6;
|
| 11 |
+
CLIENT_SIDE_TOOL_V2_EDIT_FILE = 7;
|
| 12 |
+
CLIENT_SIDE_TOOL_V2_FILE_SEARCH = 8;
|
| 13 |
+
CLIENT_SIDE_TOOL_V2_SEMANTIC_SEARCH_FULL = 9;
|
| 14 |
+
CLIENT_SIDE_TOOL_V2_CREATE_FILE = 10;
|
| 15 |
+
CLIENT_SIDE_TOOL_V2_DELETE_FILE = 11;
|
| 16 |
+
}
|
| 17 |
+
enum EmbeddingModel { // aiserver.v1.EmbeddingModel
|
| 18 |
+
EMBEDDING_MODEL_UNSPECIFIED = 0;
|
| 19 |
+
EMBEDDING_MODEL_VOYAGE_CODE_2 = 1;
|
| 20 |
+
EMBEDDING_MODEL_TEXT_EMBEDDINGS_LARGE_3 = 2;
|
| 21 |
+
EMBEDDING_MODEL_QWEN_1_5B_CUSTOM = 3;
|
| 22 |
+
}
|
| 23 |
+
enum ChunkType { // aiserver.v1.ChunkType
|
| 24 |
+
CHUNK_TYPE_UNSPECIFIED = 0;
|
| 25 |
+
CHUNK_TYPE_CODEBASE = 1;
|
| 26 |
+
CHUNK_TYPE_LONG_FILE = 2;
|
| 27 |
+
CHUNK_TYPE_DOCS = 3;
|
| 28 |
+
}
|
| 29 |
+
enum FastApplySource { // aiserver.v1.FastApplySource
|
| 30 |
+
FAST_APPLY_SOURCE_UNSPECIFIED = 0;
|
| 31 |
+
FAST_APPLY_SOURCE_COMPOSER = 1;
|
| 32 |
+
FAST_APPLY_SOURCE_CLICKED_APPLY = 2;
|
| 33 |
+
FAST_APPLY_SOURCE_CACHED_APPLY = 3;
|
| 34 |
+
}
|
| 35 |
+
enum BuiltinTool { // aiserver.v1.BuiltinTool
|
| 36 |
+
BUILTIN_TOOL_UNSPECIFIED = 0;
|
| 37 |
+
BUILTIN_TOOL_SEARCH = 1;
|
| 38 |
+
BUILTIN_TOOL_READ_CHUNK = 2;
|
| 39 |
+
BUILTIN_TOOL_GOTODEF = 3;
|
| 40 |
+
BUILTIN_TOOL_EDIT = 4;
|
| 41 |
+
BUILTIN_TOOL_UNDO_EDIT = 5;
|
| 42 |
+
BUILTIN_TOOL_END = 6;
|
| 43 |
+
BUILTIN_TOOL_NEW_FILE = 7;
|
| 44 |
+
BUILTIN_TOOL_ADD_TEST = 8;
|
| 45 |
+
BUILTIN_TOOL_RUN_TEST = 9;
|
| 46 |
+
BUILTIN_TOOL_DELETE_TEST = 10;
|
| 47 |
+
BUILTIN_TOOL_SAVE_FILE = 11;
|
| 48 |
+
BUILTIN_TOOL_GET_TESTS = 12;
|
| 49 |
+
BUILTIN_TOOL_GET_SYMBOLS = 13;
|
| 50 |
+
BUILTIN_TOOL_SEMANTIC_SEARCH = 14;
|
| 51 |
+
BUILTIN_TOOL_GET_PROJECT_STRUCTURE = 15;
|
| 52 |
+
BUILTIN_TOOL_CREATE_RM_FILES = 16;
|
| 53 |
+
BUILTIN_TOOL_RUN_TERMINAL_COMMANDS = 17;
|
| 54 |
+
BUILTIN_TOOL_NEW_EDIT = 18;
|
| 55 |
+
BUILTIN_TOOL_READ_WITH_LINTER = 19;
|
| 56 |
+
}
|
| 57 |
+
enum FeatureType { // aiserver.v1.FeatureType
|
| 58 |
+
FEATURE_TYPE_UNSPECIFIED = 0;
|
| 59 |
+
FEATURE_TYPE_EDIT = 1;
|
| 60 |
+
FEATURE_TYPE_GENERATE = 2;
|
| 61 |
+
FEATURE_TYPE_INLINE_LONG_COMPLETION = 3;
|
| 62 |
+
}
|
| 63 |
+
enum TaskStatus { // aiserver.v1.TaskStatus
|
| 64 |
+
TASK_STATUS_UNSPECIFIED = 0;
|
| 65 |
+
TASK_STATUS_RUNNING = 1;
|
| 66 |
+
TASK_STATUS_PAUSED = 2;
|
| 67 |
+
TASK_STATUS_DONE = 3;
|
| 68 |
+
TASK_STATUS_NOT_STARTED = 4;
|
| 69 |
+
}
|
| 70 |
+
enum RerankerAlgorithm { // aiserver.v1.RerankerAlgorithm
|
| 71 |
+
RERANKER_ALGORITHM_UNSPECIFIED = 0;
|
| 72 |
+
RERANKER_ALGORITHM_LULEA = 1;
|
| 73 |
+
RERANKER_ALGORITHM_UMEA = 2;
|
| 74 |
+
RERANKER_ALGORITHM_NONE = 3;
|
| 75 |
+
RERANKER_ALGORITHM_LLAMA = 4;
|
| 76 |
+
RERANKER_ALGORITHM_STARCODER_V1 = 5;
|
| 77 |
+
RERANKER_ALGORITHM_GPT_3_5_LOGPROBS = 6;
|
| 78 |
+
RERANKER_ALGORITHM_LULEA_HAIKU = 7;
|
| 79 |
+
RERANKER_ALGORITHM_COHERE = 8;
|
| 80 |
+
RERANKER_ALGORITHM_VOYAGE = 9;
|
| 81 |
+
RERANKER_ALGORITHM_VOYAGE_EMBEDS = 10;
|
| 82 |
+
RERANKER_ALGORITHM_IDENTITY = 11;
|
| 83 |
+
RERANKER_ALGORITHM_ADA_EMBEDS = 12;
|
| 84 |
+
}
|
| 85 |
+
enum RechunkerChoice { // aiserver.v1.RechunkerChoice
|
| 86 |
+
RECHUNKER_CHOICE_UNSPECIFIED = 0;
|
| 87 |
+
RECHUNKER_CHOICE_IDENTITY = 1;
|
| 88 |
+
RECHUNKER_CHOICE_600_TOKS = 2;
|
| 89 |
+
RECHUNKER_CHOICE_2400_TOKS = 3;
|
| 90 |
+
RECHUNKER_CHOICE_4000_TOKS = 4;
|
| 91 |
+
}
|
| 92 |
+
enum LintGenerator { // aiserver.v1.LintGenerator
|
| 93 |
+
LINT_GENERATOR_UNSPECIFIED = 0;
|
| 94 |
+
LINT_GENERATOR_NAIVE = 1;
|
| 95 |
+
LINT_GENERATOR_COMMENT_PIPELINE = 2;
|
| 96 |
+
LINT_GENERATOR_SIMPLE_BUG = 3;
|
| 97 |
+
LINT_GENERATOR_SIMPLE_LINT_RULES = 4;
|
| 98 |
+
}
|
| 99 |
+
enum LintDiscriminator { // aiserver.v1.LintDiscriminator
|
| 100 |
+
LINT_DISCRIMINATOR_UNSPECIFIED = 0;
|
| 101 |
+
LINT_DISCRIMINATOR_SPECIFIC_RULES = 1;
|
| 102 |
+
LINT_DISCRIMINATOR_COMPILE_ERRORS = 2;
|
| 103 |
+
LINT_DISCRIMINATOR_CHANGE_BEHAVIOR = 3;
|
| 104 |
+
LINT_DISCRIMINATOR_RELEVANCE = 4;
|
| 105 |
+
LINT_DISCRIMINATOR_USER_AWARENESS = 5;
|
| 106 |
+
LINT_DISCRIMINATOR_CORRECTNESS = 6;
|
| 107 |
+
LINT_DISCRIMINATOR_CHUNKING = 7;
|
| 108 |
+
LINT_DISCRIMINATOR_TYPO = 8;
|
| 109 |
+
LINT_DISCRIMINATOR_CONFIDENCE = 9;
|
| 110 |
+
LINT_DISCRIMINATOR_DISMISSED_BUGS = 10;
|
| 111 |
+
}
|
| 112 |
+
enum CppSource { // aiserver.v1.CppSource
|
| 113 |
+
CPP_SOURCE_UNSPECIFIED = 0;
|
| 114 |
+
CPP_SOURCE_LINE_CHANGE = 1;
|
| 115 |
+
CPP_SOURCE_TYPING = 2;
|
| 116 |
+
CPP_SOURCE_OPTION_HOLD = 3;
|
| 117 |
+
CPP_SOURCE_LINTER_ERRORS = 4;
|
| 118 |
+
CPP_SOURCE_PARAMETER_HINTS = 5;
|
| 119 |
+
CPP_SOURCE_CURSOR_PREDICTION = 6;
|
| 120 |
+
CPP_SOURCE_MANUAL_TRIGGER = 7;
|
| 121 |
+
CPP_SOURCE_EDITOR_CHANGE = 8;
|
| 122 |
+
}
|
| 123 |
+
enum ChunkingStrategy { // aiserver.v1.ChunkingStrategy
|
| 124 |
+
CHUNKING_STRATEGY_UNSPECIFIED = 0;
|
| 125 |
+
CHUNKING_STRATEGY_DEFAULT = 1;
|
| 126 |
+
}
|
| 127 |
+
message ErrorDetails { // aiserver.v1.ErrorDetails
|
| 128 |
+
enum Error { // aiserver.v1.ErrorDetails.Error
|
| 129 |
+
ERROR_UNSPECIFIED = 0;
|
| 130 |
+
ERROR_BAD_API_KEY = 1;
|
| 131 |
+
ERROR_NOT_LOGGED_IN = 2;
|
| 132 |
+
ERROR_INVALID_AUTH_ID = 3;
|
| 133 |
+
ERROR_NOT_HIGH_ENOUGH_PERMISSIONS = 4;
|
| 134 |
+
ERROR_BAD_MODEL_NAME = 5;
|
| 135 |
+
ERROR_USER_NOT_FOUND = 6;
|
| 136 |
+
ERROR_FREE_USER_RATE_LIMIT_EXCEEDED = 7;
|
| 137 |
+
ERROR_PRO_USER_RATE_LIMIT_EXCEEDED = 8;
|
| 138 |
+
ERROR_FREE_USER_USAGE_LIMIT = 9;
|
| 139 |
+
ERROR_PRO_USER_USAGE_LIMIT = 10;
|
| 140 |
+
ERROR_AUTH_TOKEN_NOT_FOUND = 11;
|
| 141 |
+
ERROR_AUTH_TOKEN_EXPIRED = 12;
|
| 142 |
+
ERROR_OPENAI = 13;
|
| 143 |
+
ERROR_OPENAI_RATE_LIMIT_EXCEEDED = 14;
|
| 144 |
+
ERROR_OPENAI_ACCOUNT_LIMIT_EXCEEDED = 15;
|
| 145 |
+
ERROR_TASK_UUID_NOT_FOUND = 16;
|
| 146 |
+
ERROR_TASK_NO_PERMISSIONS = 17;
|
| 147 |
+
ERROR_AGENT_REQUIRES_LOGIN = 18;
|
| 148 |
+
ERROR_AGENT_ENGINE_NOT_FOUND = 19;
|
| 149 |
+
ERROR_MAX_TOKENS = 20;
|
| 150 |
+
ERROR_USER_ABORTED_REQUEST = 21;
|
| 151 |
+
ERROR_GENERIC_RATE_LIMIT_EXCEEDED = 22;
|
| 152 |
+
ERROR_PRO_USER_ONLY = 23;
|
| 153 |
+
ERROR_API_KEY_NOT_SUPPORTED = 24;
|
| 154 |
+
ERROR_SLASH_EDIT_FILE_TOO_LONG = 26;
|
| 155 |
+
ERROR_FILE_UNSUPPORTED = 27;
|
| 156 |
+
ERROR_GPT_4_VISION_PREVIEW_RATE_LIMIT = 28;
|
| 157 |
+
ERROR_CUSTOM_MESSAGE = 29;
|
| 158 |
+
ERROR_OUTDATED_CLIENT = 30;
|
| 159 |
+
ERROR_CLAUDE_IMAGE_TOO_LARGE = 31;
|
| 160 |
+
ERROR_GITGRAPH_NOT_FOUND = 32;
|
| 161 |
+
ERROR_FILE_NOT_FOUND = 33;
|
| 162 |
+
ERROR_API_KEY_RATE_LIMIT = 34;
|
| 163 |
+
ERROR_DEBOUNCED = 35;
|
| 164 |
+
ERROR_BAD_REQUEST = 36;
|
| 165 |
+
ERROR_REPOSITORY_SERVICE_REPOSITORY_IS_NOT_INITIALIZED = 37;
|
| 166 |
+
ERROR_UNAUTHORIZED = 38;
|
| 167 |
+
ERROR_NOT_FOUND = 39;
|
| 168 |
+
ERROR_DEPRECATED = 40;
|
| 169 |
+
ERROR_RESOURCE_EXHAUSTED = 41;
|
| 170 |
+
}
|
| 171 |
+
Error error = 1;
|
| 172 |
+
CustomErrorDetails details = 2;
|
| 173 |
+
optional bool is_expected = 3;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
message CustomErrorDetails { // aiserver.v1.CustomErrorDetails
|
| 177 |
+
string title = 1;
|
| 178 |
+
string detail = 2;
|
| 179 |
+
optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3;
|
| 180 |
+
optional bool is_retryable = 4;
|
| 181 |
+
optional bool show_request_id = 5;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
message GetChatRequest { // aiserver.v1.GetChatRequest
|
| 185 |
+
CurrentFileInfo current_file = 1;
|
| 186 |
+
repeated ConversationMessage conversation = 2;
|
| 187 |
+
repeated RepositoryInfo repositories = 3;
|
| 188 |
+
ExplicitContext explicit_context = 4;
|
| 189 |
+
optional string workspace_root_path = 5;
|
| 190 |
+
repeated CodeBlock code_blocks = 6;
|
| 191 |
+
ModelDetails model_details = 7;
|
| 192 |
+
repeated string documentation_identifiers = 8;
|
| 193 |
+
string request_id = 9;
|
| 194 |
+
LinterErrors linter_errors = 10;
|
| 195 |
+
optional string summary = 11;
|
| 196 |
+
optional int32 summary_up_until_index = 12;
|
| 197 |
+
optional bool allow_long_file_scan = 13;
|
| 198 |
+
optional bool is_bash = 14;
|
| 199 |
+
string conversation_id = 15;
|
| 200 |
+
optional bool can_handle_filenames_after_language_ids = 16;
|
| 201 |
+
optional string use_web = 17;
|
| 202 |
+
repeated ChatQuote quotes = 18;
|
| 203 |
+
optional DebugInfo debug_info = 19;
|
| 204 |
+
optional string workspace_id = 20;
|
| 205 |
+
repeated ChatExternalLink external_links = 21;
|
| 206 |
+
repeated CommitNote commit_notes = 23;
|
| 207 |
+
optional bool long_context_mode = 22;
|
| 208 |
+
optional bool is_eval = 24;
|
| 209 |
+
optional int32 desired_max_tokens = 26;
|
| 210 |
+
ContextAST context_ast = 25;
|
| 211 |
+
optional bool is_composer = 27;
|
| 212 |
+
optional bool runnable_code_blocks = 28;
|
| 213 |
+
optional bool should_cache = 29;
|
| 214 |
+
}
|
| 215 |
+
message CurrentFileInfo { // aiserver.v1.CurrentFileInfo
|
| 216 |
+
message NotebookCell { // aiserver.v1.CurrentFileInfo.NotebookCell
|
| 217 |
+
}
|
| 218 |
+
string relative_workspace_path = 1;
|
| 219 |
+
string contents = 2;
|
| 220 |
+
bool rely_on_filesync = 18;
|
| 221 |
+
optional string sha_256_hash = 17;
|
| 222 |
+
repeated NotebookCell cells = 16;
|
| 223 |
+
repeated BM25Chunk top_chunks = 10;
|
| 224 |
+
int32 contents_start_at_line = 9;
|
| 225 |
+
CursorPosition cursor_position = 3;
|
| 226 |
+
repeated DataframeInfo dataframes = 4;
|
| 227 |
+
int32 total_number_of_lines = 8;
|
| 228 |
+
string language_id = 5;
|
| 229 |
+
CursorRange selection = 6;
|
| 230 |
+
optional int32 alternative_version_id = 11;
|
| 231 |
+
repeated Diagnostic diagnostics = 7;
|
| 232 |
+
optional int32 file_version = 14;
|
| 233 |
+
repeated int32 cell_start_lines = 15;
|
| 234 |
+
string workspace_root_path = 19;
|
| 235 |
+
}
|
| 236 |
+
message BM25Chunk { // aiserver.v1.BM25Chunk
|
| 237 |
+
string content = 1;
|
| 238 |
+
SimplestRange range = 2;
|
| 239 |
+
int32 score = 3;
|
| 240 |
+
string relative_path = 4;
|
| 241 |
+
}
|
| 242 |
+
message SimplestRange { // aiserver.v1.SimplestRange
|
| 243 |
+
int32 start_line = 1;
|
| 244 |
+
int32 end_line_inclusive = 2;
|
| 245 |
+
}
|
| 246 |
+
message CursorPosition { // aiserver.v1.CursorPosition
|
| 247 |
+
int32 line = 1;
|
| 248 |
+
int32 column = 2;
|
| 249 |
+
}
|
| 250 |
+
message DataframeInfo { // aiserver.v1.DataframeInfo
|
| 251 |
+
message Column { // aiserver.v1.DataframeInfo.Column
|
| 252 |
+
string key = 1;
|
| 253 |
+
string type = 2;
|
| 254 |
+
}
|
| 255 |
+
string name = 1;
|
| 256 |
+
string shape = 2;
|
| 257 |
+
int32 data_dimensionality = 3;
|
| 258 |
+
repeated Column columns = 6;
|
| 259 |
+
int32 row_count = 7;
|
| 260 |
+
string index_column = 8;
|
| 261 |
+
}
|
| 262 |
+
message CursorRange { // aiserver.v1.CursorRange
|
| 263 |
+
CursorPosition start_position = 1;
|
| 264 |
+
CursorPosition end_position = 2;
|
| 265 |
+
}
|
| 266 |
+
message Diagnostic { // aiserver.v1.Diagnostic
|
| 267 |
+
enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity
|
| 268 |
+
DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0;
|
| 269 |
+
DIAGNOSTIC_SEVERITY_ERROR = 1;
|
| 270 |
+
DIAGNOSTIC_SEVERITY_WARNING = 2;
|
| 271 |
+
DIAGNOSTIC_SEVERITY_INFORMATION = 3;
|
| 272 |
+
DIAGNOSTIC_SEVERITY_HINT = 4;
|
| 273 |
+
}
|
| 274 |
+
message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation
|
| 275 |
+
string message = 1;
|
| 276 |
+
CursorRange range = 2;
|
| 277 |
+
}
|
| 278 |
+
string message = 1;
|
| 279 |
+
CursorRange range = 2;
|
| 280 |
+
DiagnosticSeverity severity = 3;
|
| 281 |
+
repeated RelatedInformation related_information = 4;
|
| 282 |
+
}
|
| 283 |
+
message ConversationMessage { // aiserver.v1.ConversationMessage
|
| 284 |
+
enum MessageType { // aiserver.v1.ConversationMessage.MessageType
|
| 285 |
+
MESSAGE_TYPE_UNSPECIFIED = 0;
|
| 286 |
+
MESSAGE_TYPE_HUMAN = 1;
|
| 287 |
+
MESSAGE_TYPE_AI = 2;
|
| 288 |
+
}
|
| 289 |
+
message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk
|
| 290 |
+
enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy
|
| 291 |
+
SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
|
| 292 |
+
SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
|
| 293 |
+
SUMMARIZATION_STRATEGY_EMBEDDED = 2;
|
| 294 |
+
}
|
| 295 |
+
enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent
|
| 296 |
+
INTENT_UNSPECIFIED = 0;
|
| 297 |
+
INTENT_COMPOSER_FILE = 1;
|
| 298 |
+
INTENT_COMPRESSED_COMPOSER_FILE = 2;
|
| 299 |
+
INTENT_RECENTLY_VIEWED_FILE = 3;
|
| 300 |
+
INTENT_OUTLINE = 4;
|
| 301 |
+
INTENT_MENTIONED_FILE = 5;
|
| 302 |
+
}
|
| 303 |
+
string relative_workspace_path = 1;
|
| 304 |
+
int32 start_line_number = 2;
|
| 305 |
+
repeated string lines = 3;
|
| 306 |
+
optional SummarizationStrategy summarization_strategy = 4;
|
| 307 |
+
string language_identifier = 5;
|
| 308 |
+
optional Intent intent = 6;
|
| 309 |
+
optional bool is_final_version = 7;
|
| 310 |
+
optional bool is_first_version = 8;
|
| 311 |
+
optional bool contents_are_missing = 9;
|
| 312 |
+
}
|
| 313 |
+
message ApproximateLintError { // aiserver.v1.ConversationMessage.ApproximateLintError
|
| 314 |
+
string message = 1;
|
| 315 |
+
string value = 2;
|
| 316 |
+
int32 start_line = 3;
|
| 317 |
+
int32 end_line = 4;
|
| 318 |
+
int32 start_column = 5;
|
| 319 |
+
int32 end_column = 6;
|
| 320 |
+
}
|
| 321 |
+
message Lints { // aiserver.v1.ConversationMessage.Lints
|
| 322 |
+
GetLintsForChangeResponse lints = 1;
|
| 323 |
+
string chat_codeblock_model_value = 2;
|
| 324 |
+
}
|
| 325 |
+
message ToolResult { // aiserver.v1.ConversationMessage.ToolResult
|
| 326 |
+
message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk
|
| 327 |
+
enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy
|
| 328 |
+
SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
|
| 329 |
+
SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
|
| 330 |
+
SUMMARIZATION_STRATEGY_EMBEDDED = 2;
|
| 331 |
+
}
|
| 332 |
+
enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent
|
| 333 |
+
INTENT_UNSPECIFIED = 0;
|
| 334 |
+
INTENT_COMPOSER_FILE = 1;
|
| 335 |
+
INTENT_COMPRESSED_COMPOSER_FILE = 2;
|
| 336 |
+
INTENT_RECENTLY_VIEWED_FILE = 3;
|
| 337 |
+
INTENT_OUTLINE = 4;
|
| 338 |
+
INTENT_MENTIONED_FILE = 5;
|
| 339 |
+
}
|
| 340 |
+
string relative_workspace_path = 1;
|
| 341 |
+
int32 start_line_number = 2;
|
| 342 |
+
repeated string lines = 3;
|
| 343 |
+
optional SummarizationStrategy summarization_strategy = 4;
|
| 344 |
+
string language_identifier = 5;
|
| 345 |
+
optional Intent intent = 6;
|
| 346 |
+
optional bool is_final_version = 7;
|
| 347 |
+
optional bool is_first_version = 8;
|
| 348 |
+
optional bool contents_are_missing = 9;
|
| 349 |
+
}
|
| 350 |
+
string tool_call_id = 1;
|
| 351 |
+
string tool_name = 2;
|
| 352 |
+
uint32 tool_index = 3;
|
| 353 |
+
string args = 4;
|
| 354 |
+
string raw_args = 5;
|
| 355 |
+
repeated CodeChunk attached_code_chunks = 6;
|
| 356 |
+
optional string content = 7;
|
| 357 |
+
ClientSideToolV2Result result = 8;
|
| 358 |
+
optional ToolResultError error = 9;
|
| 359 |
+
}
|
| 360 |
+
message NotepadContext { // aiserver.v1.ConversationMessage.NotepadContext
|
| 361 |
+
message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk
|
| 362 |
+
enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy
|
| 363 |
+
SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
|
| 364 |
+
SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
|
| 365 |
+
SUMMARIZATION_STRATEGY_EMBEDDED = 2;
|
| 366 |
+
}
|
| 367 |
+
enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent
|
| 368 |
+
INTENT_UNSPECIFIED = 0;
|
| 369 |
+
INTENT_COMPOSER_FILE = 1;
|
| 370 |
+
INTENT_COMPRESSED_COMPOSER_FILE = 2;
|
| 371 |
+
INTENT_RECENTLY_VIEWED_FILE = 3;
|
| 372 |
+
INTENT_OUTLINE = 4;
|
| 373 |
+
INTENT_MENTIONED_FILE = 5;
|
| 374 |
+
}
|
| 375 |
+
string relative_workspace_path = 1;
|
| 376 |
+
int32 start_line_number = 2;
|
| 377 |
+
repeated string lines = 3;
|
| 378 |
+
optional SummarizationStrategy summarization_strategy = 4;
|
| 379 |
+
string language_identifier = 5;
|
| 380 |
+
optional Intent intent = 6;
|
| 381 |
+
optional bool is_final_version = 7;
|
| 382 |
+
optional bool is_first_version = 8;
|
| 383 |
+
optional bool contents_are_missing = 9;
|
| 384 |
+
}
|
| 385 |
+
string name = 1;
|
| 386 |
+
string text = 2;
|
| 387 |
+
repeated CodeChunk attached_code_chunks = 3;
|
| 388 |
+
repeated string attached_folders = 4;
|
| 389 |
+
repeated Commit commits = 5;
|
| 390 |
+
repeated PullRequest pull_requests = 6;
|
| 391 |
+
repeated GitDiff git_diffs = 7;
|
| 392 |
+
repeated ImageProto images = 8;
|
| 393 |
+
}
|
| 394 |
+
message EditTrailContext { // aiserver.v1.ConversationMessage.EditTrailContext
|
| 395 |
+
message EditLocation { // aiserver.v1.ConversationMessage.EditLocation
|
| 396 |
+
string relative_workspace_path = 1;
|
| 397 |
+
SimplestRange range = 3;
|
| 398 |
+
SimplestRange initial_range = 4;
|
| 399 |
+
string context_lines = 5;
|
| 400 |
+
string text = 6;
|
| 401 |
+
SimplestRange text_range = 7;
|
| 402 |
+
}
|
| 403 |
+
string unique_id = 1;
|
| 404 |
+
repeated EditLocation edit_trail_sorted = 2;
|
| 405 |
+
}
|
| 406 |
+
message RecentLocation { // aiserver.v1.ConversationMessage.RecentLocation
|
| 407 |
+
string relative_workspace_path = 1;
|
| 408 |
+
int32 line_number = 2;
|
| 409 |
+
}
|
| 410 |
+
string text = 1;
|
| 411 |
+
MessageType type = 2;
|
| 412 |
+
repeated CodeChunk attached_code_chunks = 3;
|
| 413 |
+
repeated CodeBlock codebase_context_chunks = 4;
|
| 414 |
+
repeated Commit commits = 5;
|
| 415 |
+
repeated PullRequest pull_requests = 6;
|
| 416 |
+
repeated GitDiff git_diffs = 7;
|
| 417 |
+
repeated SimpleFileDiff assistant_suggested_diffs = 8;
|
| 418 |
+
repeated InterpreterResult interpreter_results = 9;
|
| 419 |
+
repeated ImageProto images = 10;
|
| 420 |
+
repeated string attached_folders = 11;
|
| 421 |
+
repeated ApproximateLintError approximate_lint_errors = 12;
|
| 422 |
+
string bubble_id = 13;
|
| 423 |
+
optional string server_bubble_id = 32;
|
| 424 |
+
repeated FolderInfo attached_folders_new = 14;
|
| 425 |
+
repeated Lints lints = 15;
|
| 426 |
+
repeated UserResponseToSuggestedCodeBlock user_responses_to_suggested_code_blocks = 16;
|
| 427 |
+
repeated string relevant_files = 17;
|
| 428 |
+
repeated ToolResult tool_results = 18;
|
| 429 |
+
repeated NotepadContext notepads = 19;
|
| 430 |
+
optional bool is_capability_iteration = 20;
|
| 431 |
+
repeated ComposerCapabilityRequest capabilities = 21;
|
| 432 |
+
repeated EditTrailContext edit_trail_contexts = 22;
|
| 433 |
+
repeated SuggestedCodeBlock suggested_code_blocks = 23;
|
| 434 |
+
repeated RedDiff diffs_for_compressing_files = 24;
|
| 435 |
+
repeated LinterErrorsWithoutFileContents multi_file_linter_errors = 25;
|
| 436 |
+
repeated DiffHistoryData diff_histories = 26;
|
| 437 |
+
repeated CodeChunk recently_viewed_files = 27;
|
| 438 |
+
repeated RecentLocation recent_locations_history = 28;
|
| 439 |
+
bool is_agentic = 29;
|
| 440 |
+
repeated ComposerFileDiffHistory file_diff_trajectories = 30;
|
| 441 |
+
optional ConversationSummary conversation_summary = 31;
|
| 442 |
+
}
|
| 443 |
+
message CodeBlock { // aiserver.v1.CodeBlock
|
| 444 |
+
message Signatures { // aiserver.v1.CodeBlock.Signatures
|
| 445 |
+
repeated CursorRange ranges = 1;
|
| 446 |
+
}
|
| 447 |
+
string relative_workspace_path = 1;
|
| 448 |
+
optional string file_contents = 2;
|
| 449 |
+
CursorRange range = 3;
|
| 450 |
+
string contents = 4;
|
| 451 |
+
Signatures signatures = 5;
|
| 452 |
+
optional string override_contents = 6;
|
| 453 |
+
optional string original_contents = 7;
|
| 454 |
+
repeated DetailedLine detailed_lines = 8;
|
| 455 |
+
}
|
| 456 |
+
message DetailedLine { // aiserver.v1.DetailedLine
|
| 457 |
+
string text = 1;
|
| 458 |
+
float line_number = 2;
|
| 459 |
+
bool is_signature = 3;
|
| 460 |
+
}
|
| 461 |
+
message Commit { // aiserver.v1.Commit
|
| 462 |
+
string sha = 1;
|
| 463 |
+
string message = 2;
|
| 464 |
+
string description = 3;
|
| 465 |
+
repeated FileDiff diff = 4;
|
| 466 |
+
string author = 5;
|
| 467 |
+
string date = 6;
|
| 468 |
+
}
|
| 469 |
+
message FileDiff { // aiserver.v1.FileDiff
|
| 470 |
+
message Chunk { // aiserver.v1.FileDiff.Chunk
|
| 471 |
+
string content = 1;
|
| 472 |
+
repeated string lines = 2;
|
| 473 |
+
int32 old_start = 3;
|
| 474 |
+
int32 old_lines = 4;
|
| 475 |
+
int32 new_start = 5;
|
| 476 |
+
int32 new_lines = 6;
|
| 477 |
+
}
|
| 478 |
+
string from = 1;
|
| 479 |
+
string to = 2;
|
| 480 |
+
repeated Chunk chunks = 3;
|
| 481 |
+
}
|
| 482 |
+
message PullRequest { // aiserver.v1.PullRequest
|
| 483 |
+
string title = 1;
|
| 484 |
+
string body = 2;
|
| 485 |
+
repeated FileDiff diff = 3;
|
| 486 |
+
}
|
| 487 |
+
message GitDiff { // aiserver.v1.GitDiff
|
| 488 |
+
enum DiffType { // aiserver.v1.GitDiff.DiffType
|
| 489 |
+
DIFF_TYPE_UNSPECIFIED = 0;
|
| 490 |
+
DIFF_TYPE_DIFF_TO_HEAD = 1;
|
| 491 |
+
DIFF_TYPE_DIFF_FROM_BRANCH_TO_MAIN = 2;
|
| 492 |
+
}
|
| 493 |
+
repeated FileDiff diffs = 1;
|
| 494 |
+
DiffType diff_type = 2;
|
| 495 |
+
}
|
| 496 |
+
message SimpleFileDiff { // aiserver.v1.SimpleFileDiff
|
| 497 |
+
message Chunk { // aiserver.v1.SimpleFileDiff.Chunk
|
| 498 |
+
repeated string old_lines = 1;
|
| 499 |
+
repeated string new_lines = 2;
|
| 500 |
+
LineRange old_range = 3;
|
| 501 |
+
LineRange new_range = 4;
|
| 502 |
+
}
|
| 503 |
+
string relative_workspace_path = 1;
|
| 504 |
+
repeated Chunk chunks = 3;
|
| 505 |
+
}
|
| 506 |
+
message LineRange { // aiserver.v1.LineRange
|
| 507 |
+
int32 start_line_number = 1;
|
| 508 |
+
int32 end_line_number_inclusive = 2;
|
| 509 |
+
}
|
| 510 |
+
message InterpreterResult { // aiserver.v1.InterpreterResult
|
| 511 |
+
string output = 1;
|
| 512 |
+
bool success = 2;
|
| 513 |
+
}
|
| 514 |
+
message ImageProto { // aiserver.v1.ImageProto
|
| 515 |
+
message Dimension { // aiserver.v1.ImageProto.Dimension
|
| 516 |
+
int32 width = 1;
|
| 517 |
+
int32 height = 2;
|
| 518 |
+
}
|
| 519 |
+
bytes data = 1;
|
| 520 |
+
Dimension dimension = 2;
|
| 521 |
+
}
|
| 522 |
+
message FolderInfo { // aiserver.v1.FolderInfo
|
| 523 |
+
string relative_path = 1;
|
| 524 |
+
repeated FolderFileInfo files = 2;
|
| 525 |
+
}
|
| 526 |
+
message FolderFileInfo { // aiserver.v1.FolderFileInfo
|
| 527 |
+
string relative_path = 1;
|
| 528 |
+
string content = 2;
|
| 529 |
+
bool truncated = 3;
|
| 530 |
+
float score = 4;
|
| 531 |
+
}
|
| 532 |
+
message GetLintsForChangeResponse { // aiserver.v1.GetLintsForChangeResponse
|
| 533 |
+
message Lint { // aiserver.v1.GetLintsForChangeResponse.Lint
|
| 534 |
+
message QuickFix { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix
|
| 535 |
+
message Edit { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix.Edit
|
| 536 |
+
string relative_workspace_path = 1;
|
| 537 |
+
string text = 2;
|
| 538 |
+
int32 start_line_number_one_indexed = 3;
|
| 539 |
+
int32 start_column_one_indexed = 4;
|
| 540 |
+
int32 end_line_number_inclusive_one_indexed = 5;
|
| 541 |
+
int32 end_column_one_indexed = 6;
|
| 542 |
+
}
|
| 543 |
+
string message = 1;
|
| 544 |
+
string kind = 2;
|
| 545 |
+
bool is_preferred = 3;
|
| 546 |
+
repeated Edit edits = 4;
|
| 547 |
+
}
|
| 548 |
+
string message = 1;
|
| 549 |
+
string severity = 2;
|
| 550 |
+
string relative_workspace_path = 3;
|
| 551 |
+
int32 start_line_number_one_indexed = 4;
|
| 552 |
+
int32 start_column_one_indexed = 5;
|
| 553 |
+
int32 end_line_number_inclusive_one_indexed = 6;
|
| 554 |
+
int32 end_column_one_indexed = 7;
|
| 555 |
+
repeated QuickFix quick_fixes = 9;
|
| 556 |
+
}
|
| 557 |
+
repeated Lint lints = 1;
|
| 558 |
+
}
|
| 559 |
+
message UserResponseToSuggestedCodeBlock { // aiserver.v1.UserResponseToSuggestedCodeBlock
|
| 560 |
+
enum UserResponseType { // aiserver.v1.UserResponseToSuggestedCodeBlock.UserResponseType
|
| 561 |
+
USER_RESPONSE_TYPE_UNSPECIFIED = 0;
|
| 562 |
+
USER_RESPONSE_TYPE_ACCEPT = 1;
|
| 563 |
+
USER_RESPONSE_TYPE_REJECT = 2;
|
| 564 |
+
USER_RESPONSE_TYPE_MODIFY = 3;
|
| 565 |
+
}
|
| 566 |
+
UserResponseType user_response_type = 1;
|
| 567 |
+
string file_path = 2;
|
| 568 |
+
optional FileDiff user_modifications_to_suggested_code_blocks = 3;
|
| 569 |
+
}
|
| 570 |
+
message ClientSideToolV2Result { // aiserver.v1.ClientSideToolV2Result
|
| 571 |
+
ClientSideToolV2 tool = 1;
|
| 572 |
+
ReadSemsearchFilesResult read_semsearch_files_result = 2;
|
| 573 |
+
ReadFileForImportsResult read_file_for_imports_result = 3;
|
| 574 |
+
RipgrepSearchResult ripgrep_search_result = 4;
|
| 575 |
+
RunTerminalCommandResult run_terminal_command_result = 5;
|
| 576 |
+
ReadFileResult read_file_result = 6;
|
| 577 |
+
ListDirResult list_dir_result = 9;
|
| 578 |
+
EditFileResult edit_file_result = 10;
|
| 579 |
+
ToolCallFileSearchResult file_search_result = 11;
|
| 580 |
+
SemanticSearchFullResult semantic_search_full_result = 18;
|
| 581 |
+
CreateFileResult create_file_result = 19;
|
| 582 |
+
DeleteFileResult delete_file_result = 20;
|
| 583 |
+
optional ToolResultError error = 8;
|
| 584 |
+
}
|
| 585 |
+
message ReadSemsearchFilesResult { // aiserver.v1.ReadSemsearchFilesResult
|
| 586 |
+
repeated CodeResult code_results = 1;
|
| 587 |
+
}
|
| 588 |
+
message CodeResult { // aiserver.v1.CodeResult
|
| 589 |
+
CodeBlock code_block = 1;
|
| 590 |
+
float score = 2;
|
| 591 |
+
}
|
| 592 |
+
message ReadFileForImportsResult { // aiserver.v1.ReadFileForImportsResult
|
| 593 |
+
string contents = 1;
|
| 594 |
+
}
|
| 595 |
+
message RipgrepSearchResult { // aiserver.v1.RipgrepSearchResult
|
| 596 |
+
RipgrepSearchResultInternal internal = 1;
|
| 597 |
+
}
|
| 598 |
+
message RipgrepSearchResultInternal { // aiserver.v1.RipgrepSearchResultInternal
|
| 599 |
+
message IFileMatch { // aiserver.v1.RipgrepSearchResultInternal.IFileMatch
|
| 600 |
+
message ITextSearchResult { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchResult
|
| 601 |
+
message ITextSearchMatch { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchMatch
|
| 602 |
+
message ISearchRangeSetPairing { // aiserver.v1.RipgrepSearchResultInternal.ISearchRangeSetPairing
|
| 603 |
+
message ISearchRange { // aiserver.v1.RipgrepSearchResultInternal.ISearchRange
|
| 604 |
+
int32 start_line_number = 1;
|
| 605 |
+
int32 start_column = 2;
|
| 606 |
+
int32 end_line_number = 3;
|
| 607 |
+
int32 end_column = 4;
|
| 608 |
+
}
|
| 609 |
+
ISearchRange source = 1;
|
| 610 |
+
ISearchRange preview = 2;
|
| 611 |
+
}
|
| 612 |
+
optional string uri = 1;
|
| 613 |
+
repeated ISearchRangeSetPairing range_locations = 2;
|
| 614 |
+
string preview_text = 3;
|
| 615 |
+
optional int32 webview_index = 4;
|
| 616 |
+
optional string cell_fragment = 5;
|
| 617 |
+
}
|
| 618 |
+
message ITextSearchContext { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchContext
|
| 619 |
+
optional string uri = 1;
|
| 620 |
+
string text = 2;
|
| 621 |
+
int32 line_number = 3;
|
| 622 |
+
}
|
| 623 |
+
ITextSearchMatch match = 1;
|
| 624 |
+
ITextSearchContext context = 2;
|
| 625 |
+
}
|
| 626 |
+
string resource = 1;
|
| 627 |
+
repeated ITextSearchResult results = 2;
|
| 628 |
+
}
|
| 629 |
+
enum SearchCompletionExitCode { // aiserver.v1.RipgrepSearchResultInternal.SearchCompletionExitCode
|
| 630 |
+
SEARCH_COMPLETION_EXIT_CODE_UNSPECIFIED = 0;
|
| 631 |
+
SEARCH_COMPLETION_EXIT_CODE_NORMAL = 1;
|
| 632 |
+
SEARCH_COMPLETION_EXIT_CODE_NEW_SEARCH_STARTED = 2;
|
| 633 |
+
}
|
| 634 |
+
message ITextSearchCompleteMessage { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchCompleteMessage
|
| 635 |
+
enum TextSearchCompleteMessageType { // aiserver.v1.RipgrepSearchResultInternal.TextSearchCompleteMessageType
|
| 636 |
+
TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_UNSPECIFIED = 0;
|
| 637 |
+
TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_INFORMATION = 1;
|
| 638 |
+
TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_WARNING = 2;
|
| 639 |
+
}
|
| 640 |
+
string text = 1;
|
| 641 |
+
TextSearchCompleteMessageType type = 2;
|
| 642 |
+
optional bool trusted = 3;
|
| 643 |
+
}
|
| 644 |
+
message IFileSearchStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats
|
| 645 |
+
message ISearchEngineStats { // aiserver.v1.RipgrepSearchResultInternal.ISearchEngineStats
|
| 646 |
+
int32 file_walk_time = 1;
|
| 647 |
+
int32 directories_walked = 2;
|
| 648 |
+
int32 files_walked = 3;
|
| 649 |
+
int32 cmd_time = 4;
|
| 650 |
+
optional int32 cmd_result_count = 5;
|
| 651 |
+
}
|
| 652 |
+
message ICachedSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ICachedSearchStats
|
| 653 |
+
bool cache_was_resolved = 1;
|
| 654 |
+
int32 cache_lookup_time = 2;
|
| 655 |
+
int32 cache_filter_time = 3;
|
| 656 |
+
int32 cache_entry_count = 4;
|
| 657 |
+
}
|
| 658 |
+
message IFileSearchProviderStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchProviderStats
|
| 659 |
+
int32 provider_time = 1;
|
| 660 |
+
int32 post_process_time = 2;
|
| 661 |
+
}
|
| 662 |
+
enum FileSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats.FileSearchProviderType
|
| 663 |
+
FILE_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0;
|
| 664 |
+
FILE_SEARCH_PROVIDER_TYPE_FILE_SEARCH_PROVIDER = 1;
|
| 665 |
+
FILE_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2;
|
| 666 |
+
}
|
| 667 |
+
bool from_cache = 1;
|
| 668 |
+
ISearchEngineStats search_engine_stats = 2;
|
| 669 |
+
ICachedSearchStats cached_search_stats = 3;
|
| 670 |
+
IFileSearchProviderStats file_search_provider_stats = 4;
|
| 671 |
+
int32 result_count = 5;
|
| 672 |
+
FileSearchProviderType type = 6;
|
| 673 |
+
optional int32 sorting_time = 7;
|
| 674 |
+
}
|
| 675 |
+
message ITextSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats
|
| 676 |
+
enum TextSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats.TextSearchProviderType
|
| 677 |
+
TEXT_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0;
|
| 678 |
+
TEXT_SEARCH_PROVIDER_TYPE_TEXT_SEARCH_PROVIDER = 1;
|
| 679 |
+
TEXT_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2;
|
| 680 |
+
TEXT_SEARCH_PROVIDER_TYPE_AI_TEXT_SEARCH_PROVIDER = 3;
|
| 681 |
+
}
|
| 682 |
+
TextSearchProviderType type = 1;
|
| 683 |
+
}
|
| 684 |
+
repeated IFileMatch results = 1;
|
| 685 |
+
optional SearchCompletionExitCode exit = 2;
|
| 686 |
+
optional bool limit_hit = 3;
|
| 687 |
+
repeated ITextSearchCompleteMessage messages = 4;
|
| 688 |
+
IFileSearchStats file_search_stats = 5;
|
| 689 |
+
ITextSearchStats text_search_stats = 6;
|
| 690 |
+
}
|
| 691 |
+
message RunTerminalCommandResult { // aiserver.v1.RunTerminalCommandResult
|
| 692 |
+
string output = 1;
|
| 693 |
+
int32 exit_code = 2;
|
| 694 |
+
optional bool rejected = 3;
|
| 695 |
+
bool popped_out_into_background = 4;
|
| 696 |
+
}
|
| 697 |
+
message ReadFileResult { // aiserver.v1.ReadFileResult
|
| 698 |
+
string contents = 1;
|
| 699 |
+
bool did_downgrade_to_line_range = 2;
|
| 700 |
+
bool did_shorten_line_range = 3;
|
| 701 |
+
bool did_set_default_line_range = 4;
|
| 702 |
+
optional string full_file_contents = 5;
|
| 703 |
+
optional string outline = 6;
|
| 704 |
+
optional int32 start_line_one_indexed = 7;
|
| 705 |
+
optional int32 end_line_one_indexed_inclusive = 8;
|
| 706 |
+
string relative_workspace_path = 9;
|
| 707 |
+
bool did_shorten_char_range = 10;
|
| 708 |
+
}
|
| 709 |
+
message ListDirResult { // aiserver.v1.ListDirResult
|
| 710 |
+
message File { // aiserver.v1.ListDirResult.File
|
| 711 |
+
message Timestamp { // google.protobuf.Timestamp
|
| 712 |
+
int64 seconds = 1;
|
| 713 |
+
int32 nanos = 2;
|
| 714 |
+
}
|
| 715 |
+
string name = 1;
|
| 716 |
+
bool is_directory = 2;
|
| 717 |
+
optional int64 size = 3;
|
| 718 |
+
optional Timestamp last_modified = 4;
|
| 719 |
+
optional int32 num_children = 5;
|
| 720 |
+
optional int32 num_lines = 6;
|
| 721 |
+
}
|
| 722 |
+
repeated File files = 1;
|
| 723 |
+
string directory_relative_workspace_path = 2;
|
| 724 |
+
}
|
| 725 |
+
message EditFileResult { // aiserver.v1.EditFileResult
|
| 726 |
+
message FileDiff { // aiserver.v1.EditFileResult.FileDiff
|
| 727 |
+
message ChunkDiff { // aiserver.v1.EditFileResult.FileDiff.ChunkDiff
|
| 728 |
+
string diff_string = 1;
|
| 729 |
+
int32 old_start = 2;
|
| 730 |
+
int32 new_start = 3;
|
| 731 |
+
int32 old_lines = 4;
|
| 732 |
+
int32 new_lines = 5;
|
| 733 |
+
int32 lines_removed = 6;
|
| 734 |
+
int32 lines_added = 7;
|
| 735 |
+
}
|
| 736 |
+
enum Editor { // aiserver.v1.EditFileResult.FileDiff.Editor
|
| 737 |
+
EDITOR_UNSPECIFIED = 0;
|
| 738 |
+
EDITOR_AI = 1;
|
| 739 |
+
EDITOR_HUMAN = 2;
|
| 740 |
+
}
|
| 741 |
+
repeated ChunkDiff chunks = 1;
|
| 742 |
+
Editor editor = 2;
|
| 743 |
+
bool hit_timeout = 3;
|
| 744 |
+
}
|
| 745 |
+
FileDiff diff = 1;
|
| 746 |
+
bool is_applied = 2;
|
| 747 |
+
bool apply_failed = 3;
|
| 748 |
+
}
|
| 749 |
+
message ToolCallFileSearchResult { // aiserver.v1.ToolCallFileSearchResult
|
| 750 |
+
message File { // aiserver.v1.ToolCallFileSearchResult.File
|
| 751 |
+
string uri = 1;
|
| 752 |
+
}
|
| 753 |
+
repeated File files = 1;
|
| 754 |
+
optional bool limit_hit = 2;
|
| 755 |
+
int32 num_results = 3;
|
| 756 |
+
}
|
| 757 |
+
message SemanticSearchFullResult { // aiserver.v1.SemanticSearchFullResult
|
| 758 |
+
repeated CodeResult code_results = 1;
|
| 759 |
+
}
|
| 760 |
+
message CreateFileResult { // aiserver.v1.CreateFileResult
|
| 761 |
+
bool file_created_successfully = 1;
|
| 762 |
+
bool file_already_exists = 2;
|
| 763 |
+
}
|
| 764 |
+
message DeleteFileResult { // aiserver.v1.DeleteFileResult
|
| 765 |
+
bool rejected = 1;
|
| 766 |
+
bool file_non_existent = 2;
|
| 767 |
+
bool file_deleted_successfully = 3;
|
| 768 |
+
}
|
| 769 |
+
message ToolResultError { // aiserver.v1.ToolResultError
|
| 770 |
+
string client_visible_error_message = 1;
|
| 771 |
+
string model_visible_error_message = 2;
|
| 772 |
+
}
|
| 773 |
+
message ComposerCapabilityRequest { // aiserver.v1.ComposerCapabilityRequest
|
| 774 |
+
enum ComposerCapabilityType { // aiserver.v1.ComposerCapabilityRequest.ComposerCapabilityType
|
| 775 |
+
COMPOSER_CAPABILITY_TYPE_UNSPECIFIED = 0;
|
| 776 |
+
COMPOSER_CAPABILITY_TYPE_LOOP_ON_LINTS = 1;
|
| 777 |
+
COMPOSER_CAPABILITY_TYPE_LOOP_ON_TESTS = 2;
|
| 778 |
+
COMPOSER_CAPABILITY_TYPE_MEGA_PLANNER = 3;
|
| 779 |
+
COMPOSER_CAPABILITY_TYPE_LOOP_ON_COMMAND = 4;
|
| 780 |
+
COMPOSER_CAPABILITY_TYPE_TOOL_CALL = 5;
|
| 781 |
+
COMPOSER_CAPABILITY_TYPE_DIFF_REVIEW = 6;
|
| 782 |
+
COMPOSER_CAPABILITY_TYPE_CONTEXT_PICKING = 7;
|
| 783 |
+
COMPOSER_CAPABILITY_TYPE_EDIT_TRAIL = 8;
|
| 784 |
+
COMPOSER_CAPABILITY_TYPE_AUTO_CONTEXT = 9;
|
| 785 |
+
COMPOSER_CAPABILITY_TYPE_CONTEXT_PLANNER = 10;
|
| 786 |
+
COMPOSER_CAPABILITY_TYPE_DIFF_HISTORY = 11;
|
| 787 |
+
COMPOSER_CAPABILITY_TYPE_REMEMBER_THIS = 12;
|
| 788 |
+
COMPOSER_CAPABILITY_TYPE_DECOMPOSER = 13;
|
| 789 |
+
COMPOSER_CAPABILITY_TYPE_USES_CODEBASE = 14;
|
| 790 |
+
COMPOSER_CAPABILITY_TYPE_TOOL_FORMER = 15;
|
| 791 |
+
}
|
| 792 |
+
message LoopOnLintsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnLintsCapability
|
| 793 |
+
repeated LinterErrors linter_errors = 1;
|
| 794 |
+
optional string custom_instructions = 2;
|
| 795 |
+
}
|
| 796 |
+
message LoopOnTestsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnTestsCapability
|
| 797 |
+
repeated string test_names = 1;
|
| 798 |
+
optional string custom_instructions = 2;
|
| 799 |
+
}
|
| 800 |
+
message MegaPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.MegaPlannerCapability
|
| 801 |
+
optional string custom_instructions = 1;
|
| 802 |
+
}
|
| 803 |
+
message LoopOnCommandCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnCommandCapability
|
| 804 |
+
string command = 1;
|
| 805 |
+
optional string custom_instructions = 2;
|
| 806 |
+
optional string output = 3;
|
| 807 |
+
optional int32 exit_code = 4;
|
| 808 |
+
}
|
| 809 |
+
message ToolCallCapability { // aiserver.v1.ComposerCapabilityRequest.ToolCallCapability
|
| 810 |
+
message ToolSchema { // aiserver.v1.ComposerCapabilityRequest.ToolSchema
|
| 811 |
+
enum ToolType { // aiserver.v1.ComposerCapabilityRequest.ToolType
|
| 812 |
+
TOOL_TYPE_UNSPECIFIED = 0;
|
| 813 |
+
TOOL_TYPE_ADD_FILE_TO_CONTEXT = 1;
|
| 814 |
+
TOOL_TYPE_RUN_TERMINAL_COMMAND = 2;
|
| 815 |
+
TOOL_TYPE_ITERATE = 3;
|
| 816 |
+
TOOL_TYPE_REMOVE_FILE_FROM_CONTEXT = 4;
|
| 817 |
+
TOOL_TYPE_SEMANTIC_SEARCH_CODEBASE = 5;
|
| 818 |
+
}
|
| 819 |
+
ToolType type = 1;
|
| 820 |
+
string name = 2;
|
| 821 |
+
repeated string required = 4;
|
| 822 |
+
}
|
| 823 |
+
optional string custom_instructions = 1;
|
| 824 |
+
repeated ToolSchema tool_schemas = 2;
|
| 825 |
+
repeated string relevant_files = 3;
|
| 826 |
+
repeated string files_in_context = 4;
|
| 827 |
+
repeated string semantic_search_files = 5;
|
| 828 |
+
}
|
| 829 |
+
message DiffReviewCapability { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability
|
| 830 |
+
message SimpleFileDiff { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff
|
| 831 |
+
message Chunk { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff.Chunk
|
| 832 |
+
repeated string old_lines = 1;
|
| 833 |
+
repeated string new_lines = 2;
|
| 834 |
+
LineRange old_range = 3;
|
| 835 |
+
LineRange new_range = 4;
|
| 836 |
+
}
|
| 837 |
+
string relative_workspace_path = 1;
|
| 838 |
+
repeated Chunk chunks = 3;
|
| 839 |
+
}
|
| 840 |
+
optional string custom_instructions = 1;
|
| 841 |
+
repeated SimpleFileDiff diffs = 2;
|
| 842 |
+
}
|
| 843 |
+
message ContextPickingCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPickingCapability
|
| 844 |
+
optional string custom_instructions = 1;
|
| 845 |
+
repeated string potential_context_files = 2;
|
| 846 |
+
repeated CodeChunk potential_context_code_chunks = 3;
|
| 847 |
+
repeated string files_in_context = 4;
|
| 848 |
+
}
|
| 849 |
+
message EditTrailCapability { // aiserver.v1.ComposerCapabilityRequest.EditTrailCapability
|
| 850 |
+
optional string custom_instructions = 1;
|
| 851 |
+
}
|
| 852 |
+
message AutoContextCapability { // aiserver.v1.ComposerCapabilityRequest.AutoContextCapability
|
| 853 |
+
optional string custom_instructions = 1;
|
| 854 |
+
repeated string additional_files = 2;
|
| 855 |
+
}
|
| 856 |
+
message ContextPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPlannerCapability
|
| 857 |
+
optional string custom_instructions = 1;
|
| 858 |
+
repeated CodeChunk attached_code_chunks = 2;
|
| 859 |
+
}
|
| 860 |
+
message RememberThisCapability { // aiserver.v1.ComposerCapabilityRequest.RememberThisCapability
|
| 861 |
+
optional string custom_instructions = 1;
|
| 862 |
+
string memory = 2;
|
| 863 |
+
}
|
| 864 |
+
message DecomposerCapability { // aiserver.v1.ComposerCapabilityRequest.DecomposerCapability
|
| 865 |
+
optional string custom_instructions = 1;
|
| 866 |
+
}
|
| 867 |
+
ComposerCapabilityType type = 1;
|
| 868 |
+
LoopOnLintsCapability loop_on_lints = 2;
|
| 869 |
+
LoopOnTestsCapability loop_on_tests = 3;
|
| 870 |
+
MegaPlannerCapability mega_planner = 4;
|
| 871 |
+
LoopOnCommandCapability loop_on_command = 5;
|
| 872 |
+
ToolCallCapability tool_call = 6;
|
| 873 |
+
DiffReviewCapability diff_review = 7;
|
| 874 |
+
ContextPickingCapability context_picking = 8;
|
| 875 |
+
EditTrailCapability edit_trail = 9;
|
| 876 |
+
AutoContextCapability auto_context = 10;
|
| 877 |
+
ContextPlannerCapability context_planner = 11;
|
| 878 |
+
RememberThisCapability remember_this = 12;
|
| 879 |
+
DecomposerCapability decomposer = 13;
|
| 880 |
+
}
|
| 881 |
+
message LinterErrors { // aiserver.v1.LinterErrors
|
| 882 |
+
string relative_workspace_path = 1;
|
| 883 |
+
repeated LinterError errors = 2;
|
| 884 |
+
string file_contents = 3;
|
| 885 |
+
}
|
| 886 |
+
message LinterError { // aiserver.v1.LinterError
|
| 887 |
+
message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation
|
| 888 |
+
string message = 1;
|
| 889 |
+
CursorRange range = 2;
|
| 890 |
+
}
|
| 891 |
+
enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity
|
| 892 |
+
DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0;
|
| 893 |
+
DIAGNOSTIC_SEVERITY_ERROR = 1;
|
| 894 |
+
DIAGNOSTIC_SEVERITY_WARNING = 2;
|
| 895 |
+
DIAGNOSTIC_SEVERITY_INFORMATION = 3;
|
| 896 |
+
DIAGNOSTIC_SEVERITY_HINT = 4;
|
| 897 |
+
}
|
| 898 |
+
string message = 1;
|
| 899 |
+
CursorRange range = 2;
|
| 900 |
+
optional string source = 3;
|
| 901 |
+
repeated RelatedInformation related_information = 4;
|
| 902 |
+
optional DiagnosticSeverity severity = 5;
|
| 903 |
+
}
|
| 904 |
+
message CodeChunk { // aiserver.v1.CodeChunk
|
| 905 |
+
enum SummarizationStrategy { // aiserver.v1.CodeChunk.SummarizationStrategy
|
| 906 |
+
SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
|
| 907 |
+
SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
|
| 908 |
+
SUMMARIZATION_STRATEGY_EMBEDDED = 2;
|
| 909 |
+
}
|
| 910 |
+
enum Intent { // aiserver.v1.CodeChunk.Intent
|
| 911 |
+
INTENT_UNSPECIFIED = 0;
|
| 912 |
+
INTENT_COMPOSER_FILE = 1;
|
| 913 |
+
INTENT_COMPRESSED_COMPOSER_FILE = 2;
|
| 914 |
+
}
|
| 915 |
+
string relative_workspace_path = 1;
|
| 916 |
+
int32 start_line_number = 2;
|
| 917 |
+
repeated string lines = 3;
|
| 918 |
+
optional SummarizationStrategy summarization_strategy = 4;
|
| 919 |
+
string language_identifier = 5;
|
| 920 |
+
optional Intent intent = 6;
|
| 921 |
+
optional bool is_final_version = 7;
|
| 922 |
+
optional bool is_first_version = 8;
|
| 923 |
+
}
|
| 924 |
+
message SuggestedCodeBlock { // aiserver.v1.SuggestedCodeBlock
|
| 925 |
+
string relative_workspace_path = 1;
|
| 926 |
+
}
|
| 927 |
+
message RedDiff { // aiserver.v1.RedDiff
|
| 928 |
+
string relative_workspace_path = 1;
|
| 929 |
+
repeated SimplestRange red_ranges = 2;
|
| 930 |
+
repeated SimplestRange red_ranges_reversed = 3;
|
| 931 |
+
string start_hash = 4;
|
| 932 |
+
string end_hash = 5;
|
| 933 |
+
}
|
| 934 |
+
message LinterErrorsWithoutFileContents { // aiserver.v1.LinterErrorsWithoutFileContents
|
| 935 |
+
string relative_workspace_path = 1;
|
| 936 |
+
repeated LinterError errors = 2;
|
| 937 |
+
}
|
| 938 |
+
message DiffHistoryData { // aiserver.v1.DiffHistoryData
|
| 939 |
+
string relative_workspace_path = 1;
|
| 940 |
+
repeated ComposerFileDiff diffs = 2;
|
| 941 |
+
double timestamp = 3;
|
| 942 |
+
string unique_id = 4;
|
| 943 |
+
ComposerFileDiff start_to_end_diff = 5;
|
| 944 |
+
}
|
| 945 |
+
message ComposerFileDiff { // aiserver.v1.ComposerFileDiff
|
| 946 |
+
message ChunkDiff { // aiserver.v1.ComposerFileDiff.ChunkDiff
|
| 947 |
+
string diff_string = 1;
|
| 948 |
+
int32 old_start = 2;
|
| 949 |
+
int32 new_start = 3;
|
| 950 |
+
int32 old_lines = 4;
|
| 951 |
+
int32 new_lines = 5;
|
| 952 |
+
int32 lines_removed = 6;
|
| 953 |
+
int32 lines_added = 7;
|
| 954 |
+
}
|
| 955 |
+
enum Editor { // aiserver.v1.ComposerFileDiff.Editor
|
| 956 |
+
EDITOR_UNSPECIFIED = 0;
|
| 957 |
+
EDITOR_AI = 1;
|
| 958 |
+
EDITOR_HUMAN = 2;
|
| 959 |
+
}
|
| 960 |
+
repeated ChunkDiff chunks = 1;
|
| 961 |
+
Editor editor = 2;
|
| 962 |
+
bool hit_timeout = 3;
|
| 963 |
+
}
|
| 964 |
+
message ComposerFileDiffHistory { // aiserver.v1.ComposerFileDiffHistory
|
| 965 |
+
string file_name = 1;
|
| 966 |
+
repeated string diff_history = 2;
|
| 967 |
+
repeated double diff_history_timestamps = 3;
|
| 968 |
+
}
|
| 969 |
+
message ConversationSummary { // aiserver.v1.ConversationSummary
|
| 970 |
+
string summary = 1;
|
| 971 |
+
string truncation_last_bubble_id_inclusive = 2;
|
| 972 |
+
string client_should_start_sending_from_inclusive_bubble_id = 3;
|
| 973 |
+
}
|
| 974 |
+
message RepositoryInfo { // aiserver.v1.RepositoryInfo
|
| 975 |
+
string relative_workspace_path = 1;
|
| 976 |
+
repeated string remote_urls = 2;
|
| 977 |
+
repeated string remote_names = 3;
|
| 978 |
+
string repo_name = 4;
|
| 979 |
+
string repo_owner = 5;
|
| 980 |
+
bool is_tracked = 6;
|
| 981 |
+
bool is_local = 7;
|
| 982 |
+
optional int32 num_files = 8;
|
| 983 |
+
optional double orthogonal_transform_seed = 9;
|
| 984 |
+
optional EmbeddingModel preferred_embedding_model = 10;
|
| 985 |
+
}
|
| 986 |
+
message ExplicitContext { // aiserver.v1.ExplicitContext
|
| 987 |
+
string context = 1;
|
| 988 |
+
optional string repo_context = 2;
|
| 989 |
+
}
|
| 990 |
+
message ModelDetails { // aiserver.v1.ModelDetails
|
| 991 |
+
optional string model_name = 1;
|
| 992 |
+
optional string api_key = 2;
|
| 993 |
+
optional bool enable_ghost_mode = 3;
|
| 994 |
+
optional AzureState azure_state = 4;
|
| 995 |
+
optional bool enable_slow_pool = 5;
|
| 996 |
+
optional string openai_api_base_url = 6;
|
| 997 |
+
}
|
| 998 |
+
message AzureState { // aiserver.v1.AzureState
|
| 999 |
+
string api_key = 1;
|
| 1000 |
+
string base_url = 2;
|
| 1001 |
+
string deployment = 3;
|
| 1002 |
+
bool use_azure = 4;
|
| 1003 |
+
}
|
| 1004 |
+
message ChatQuote { // aiserver.v1.ChatQuote
|
| 1005 |
+
string markdown = 1;
|
| 1006 |
+
string bubble_id = 2;
|
| 1007 |
+
int32 section_index = 3;
|
| 1008 |
+
}
|
| 1009 |
+
message DebugInfo { // aiserver.v1.DebugInfo
|
| 1010 |
+
message Breakpoint { // aiserver.v1.DebugInfo.Breakpoint
|
| 1011 |
+
string relative_workspace_path = 1;
|
| 1012 |
+
int32 line_number = 2;
|
| 1013 |
+
repeated string lines_before_breakpoint = 3;
|
| 1014 |
+
repeated string lines_after_breakpoint = 4;
|
| 1015 |
+
optional string exception_info = 5;
|
| 1016 |
+
}
|
| 1017 |
+
message CallStackFrame { // aiserver.v1.DebugInfo.CallStackFrame
|
| 1018 |
+
message Scope { // aiserver.v1.DebugInfo.Scope
|
| 1019 |
+
message Variable { // aiserver.v1.DebugInfo.Variable
|
| 1020 |
+
string name = 1;
|
| 1021 |
+
string value = 2;
|
| 1022 |
+
optional string type = 3;
|
| 1023 |
+
}
|
| 1024 |
+
string name = 1;
|
| 1025 |
+
repeated Variable variables = 2;
|
| 1026 |
+
}
|
| 1027 |
+
string relative_workspace_path = 1;
|
| 1028 |
+
int32 line_number = 2;
|
| 1029 |
+
string function_name = 3;
|
| 1030 |
+
repeated Scope scopes = 4;
|
| 1031 |
+
}
|
| 1032 |
+
Breakpoint breakpoint = 1;
|
| 1033 |
+
repeated CallStackFrame call_stack = 2;
|
| 1034 |
+
repeated CodeBlock history = 3;
|
| 1035 |
+
}
|
| 1036 |
+
message ChatExternalLink { // aiserver.v1.ChatExternalLink
|
| 1037 |
+
string url = 1;
|
| 1038 |
+
string uuid = 2;
|
| 1039 |
+
}
|
| 1040 |
+
message CommitNote { // aiserver.v1.CommitNote
|
| 1041 |
+
string note = 1;
|
| 1042 |
+
string commit_hash = 2;
|
| 1043 |
+
}
|
| 1044 |
+
message ContextAST { // aiserver.v1.ContextAST
|
| 1045 |
+
repeated ContainerTree files = 1;
|
| 1046 |
+
}
|
| 1047 |
+
message ContainerTree { // aiserver.v1.ContainerTree
|
| 1048 |
+
string relative_workspace_path = 1;
|
| 1049 |
+
repeated ContainerTreeNode nodes = 2;
|
| 1050 |
+
}
|
| 1051 |
+
message ContainerTreeNode { // aiserver.v1.ContainerTreeNode
|
| 1052 |
+
message Container { // aiserver.v1.ContainerTreeNode.Container
|
| 1053 |
+
message Reference { // aiserver.v1.ContainerTreeNode.Reference
|
| 1054 |
+
string value = 1;
|
| 1055 |
+
string relative_workspace_path = 2;
|
| 1056 |
+
}
|
| 1057 |
+
string doc_string = 1;
|
| 1058 |
+
string header = 2;
|
| 1059 |
+
string trailer = 3;
|
| 1060 |
+
repeated ContainerTreeNode children = 5;
|
| 1061 |
+
repeated Reference references = 6;
|
| 1062 |
+
double score = 7;
|
| 1063 |
+
}
|
| 1064 |
+
message Blob { // aiserver.v1.ContainerTreeNode.Blob
|
| 1065 |
+
optional string value = 1;
|
| 1066 |
+
}
|
| 1067 |
+
message Symbol { // aiserver.v1.ContainerTreeNode.Symbol
|
| 1068 |
+
message Reference { // aiserver.v1.ContainerTreeNode.Reference
|
| 1069 |
+
string value = 1;
|
| 1070 |
+
string relative_workspace_path = 2;
|
| 1071 |
+
}
|
| 1072 |
+
string doc_string = 1;
|
| 1073 |
+
string value = 2;
|
| 1074 |
+
repeated Reference references = 6;
|
| 1075 |
+
double score = 7;
|
| 1076 |
+
}
|
| 1077 |
+
Container container = 1;
|
| 1078 |
+
Blob blob = 2;
|
| 1079 |
+
Symbol symbol = 3;
|
| 1080 |
+
}
|
| 1081 |
+
message StreamChatResponse { // aiserver.v1.StreamChatResponse
|
| 1082 |
+
message ChunkIdentity { // aiserver.v1.StreamChatResponse.ChunkIdentity
|
| 1083 |
+
string file_name = 1;
|
| 1084 |
+
int32 start_line = 2;
|
| 1085 |
+
int32 end_line = 3;
|
| 1086 |
+
string text = 4;
|
| 1087 |
+
ChunkType chunk_type = 5;
|
| 1088 |
+
}
|
| 1089 |
+
string text = 1;
|
| 1090 |
+
optional string server_bubble_id = 22;
|
| 1091 |
+
optional string debugging_only_chat_prompt = 2;
|
| 1092 |
+
optional int32 debugging_only_token_count = 3;
|
| 1093 |
+
DocumentationCitation document_citation = 4;
|
| 1094 |
+
optional string filled_prompt = 5;
|
| 1095 |
+
optional bool is_big_file = 6;
|
| 1096 |
+
optional string intermediate_text = 7;
|
| 1097 |
+
optional bool is_using_slow_request = 10;
|
| 1098 |
+
optional ChunkIdentity chunk_identity = 8;
|
| 1099 |
+
optional DocsReference docs_reference = 9;
|
| 1100 |
+
optional WebCitation web_citation = 11;
|
| 1101 |
+
optional StatusUpdates status_updates = 12;
|
| 1102 |
+
optional ServerTimingInfo timing_info = 13;
|
| 1103 |
+
optional SymbolLink symbol_link = 14;
|
| 1104 |
+
optional FileLink file_link = 15;
|
| 1105 |
+
optional ConversationSummary conversation_summary = 16;
|
| 1106 |
+
optional ServiceStatusUpdate service_status_update = 17;
|
| 1107 |
+
}
|
| 1108 |
+
message DocumentationCitation { // aiserver.v1.DocumentationCitation
|
| 1109 |
+
repeated DocumentationChunk chunks = 1;
|
| 1110 |
+
}
|
| 1111 |
+
message DocumentationChunk { // aiserver.v1.DocumentationChunk
|
| 1112 |
+
string doc_name = 1;
|
| 1113 |
+
string page_url = 2;
|
| 1114 |
+
string documentation_chunk = 3;
|
| 1115 |
+
float score = 4;
|
| 1116 |
+
string page_title = 5;
|
| 1117 |
+
}
|
| 1118 |
+
message DocsReference { // aiserver.v1.DocsReference
|
| 1119 |
+
string title = 1;
|
| 1120 |
+
string url = 2;
|
| 1121 |
+
}
|
| 1122 |
+
message WebCitation { // aiserver.v1.WebCitation
|
| 1123 |
+
repeated WebReference references = 1;
|
| 1124 |
+
}
|
| 1125 |
+
message WebReference { // aiserver.v1.WebReference
|
| 1126 |
+
string title = 2;
|
| 1127 |
+
string url = 1;
|
| 1128 |
+
}
|
| 1129 |
+
message StatusUpdates { // aiserver.v1.StatusUpdates
|
| 1130 |
+
repeated StatusUpdate updates = 1;
|
| 1131 |
+
}
|
| 1132 |
+
message StatusUpdate { // aiserver.v1.StatusUpdate
|
| 1133 |
+
string message = 1;
|
| 1134 |
+
optional string metadata = 2;
|
| 1135 |
+
}
|
| 1136 |
+
message ServerTimingInfo { // aiserver.v1.ServerTimingInfo
|
| 1137 |
+
double server_start_time = 1;
|
| 1138 |
+
double server_first_token_time = 2;
|
| 1139 |
+
double server_request_sent_time = 3;
|
| 1140 |
+
double server_end_time = 4;
|
| 1141 |
+
}
|
| 1142 |
+
message SymbolLink { // aiserver.v1.SymbolLink
|
| 1143 |
+
string symbol_name = 1;
|
| 1144 |
+
string symbol_search_string = 2;
|
| 1145 |
+
string relative_workspace_path = 3;
|
| 1146 |
+
int32 rough_line_number = 4;
|
| 1147 |
+
}
|
| 1148 |
+
message FileLink { // aiserver.v1.FileLink
|
| 1149 |
+
string display_name = 1;
|
| 1150 |
+
string relative_workspace_path = 2;
|
| 1151 |
+
}
|
| 1152 |
+
message ServiceStatusUpdate { // aiserver.v1.ServiceStatusUpdate
|
| 1153 |
+
string message = 1;
|
| 1154 |
+
string codicon = 2;
|
| 1155 |
+
optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3;
|
| 1156 |
+
}
|
src/chat/config.rs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::AppConfig;
|
| 2 |
+
|
| 3 |
+
include!(concat!(env!("OUT_DIR"), "/key.rs"));
|
| 4 |
+
|
| 5 |
+
impl KeyConfig {
|
| 6 |
+
pub fn new_with_global() -> Self {
|
| 7 |
+
Self {
|
| 8 |
+
auth_token: None,
|
| 9 |
+
disable_vision: Some(AppConfig::get_vision_ability().is_none()),
|
| 10 |
+
enable_slow_pool: Some(AppConfig::get_slow_pool()),
|
| 11 |
+
usage_check_models: None,
|
| 12 |
+
include_web_references: Some(AppConfig::get_web_refs()),
|
| 13 |
+
}
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
pub fn copy_without_auth_token(&self, config: &mut Self) {
|
| 17 |
+
if self.disable_vision.is_some() {
|
| 18 |
+
config.disable_vision = self.disable_vision;
|
| 19 |
+
}
|
| 20 |
+
if self.enable_slow_pool.is_some() {
|
| 21 |
+
config.enable_slow_pool = self.enable_slow_pool;
|
| 22 |
+
}
|
| 23 |
+
if self.usage_check_models.is_some() {
|
| 24 |
+
config.usage_check_models = self.usage_check_models.clone();
|
| 25 |
+
}
|
| 26 |
+
if self.include_web_references.is_some() {
|
| 27 |
+
config.include_web_references = self.include_web_references;
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
}
|
src/chat/config/key.proto
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
syntax = "proto3";
|
| 2 |
+
|
| 3 |
+
package key;
|
| 4 |
+
|
| 5 |
+
// 动态配置的 API KEY
|
| 6 |
+
message KeyConfig {
|
| 7 |
+
// 认证令牌信息
|
| 8 |
+
message TokenInfo {
|
| 9 |
+
string sub = 1; // 用户标识符
|
| 10 |
+
int64 exp = 2; // 过期时间(Unix 时间戳)
|
| 11 |
+
string randomness = 3; // 随机字符串
|
| 12 |
+
string signature = 4; // 签名
|
| 13 |
+
bytes machine_id = 5; // 机器ID的SHA256哈希值
|
| 14 |
+
bytes mac_id = 6; // MAC地址的SHA256哈希值
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
// 认证令牌(必需)
|
| 18 |
+
TokenInfo auth_token = 1;
|
| 19 |
+
|
| 20 |
+
// 是否禁用图片处理能力
|
| 21 |
+
optional bool disable_vision = 4;
|
| 22 |
+
|
| 23 |
+
// 是否启用慢速池
|
| 24 |
+
optional bool enable_slow_pool = 5;
|
| 25 |
+
|
| 26 |
+
// 使用量检查模型规则
|
| 27 |
+
message UsageCheckModel {
|
| 28 |
+
// 检查类型
|
| 29 |
+
enum Type {
|
| 30 |
+
TYPE_DEFAULT = 0; // 未指定
|
| 31 |
+
TYPE_DISABLED = 1; // 禁用
|
| 32 |
+
TYPE_ALL = 2; // 全部
|
| 33 |
+
TYPE_CUSTOM = 3; // 自定义列表
|
| 34 |
+
}
|
| 35 |
+
Type type = 1; // 检查类型
|
| 36 |
+
repeated string model_ids = 2; // 模型 ID 列表,当 type 为 TYPE_CUSTOM 时生效
|
| 37 |
+
}
|
| 38 |
+
// 使用量检查模型规则
|
| 39 |
+
optional UsageCheckModel usage_check_models = 6;
|
| 40 |
+
|
| 41 |
+
// 包含网络引用
|
| 42 |
+
optional bool include_web_references = 7;
|
| 43 |
+
|
| 44 |
+
// 密码SHA256哈希值
|
| 45 |
+
// bytes secret = 2;
|
| 46 |
+
}
|
src/chat/constant.rs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use super::model::Model;
|
| 2 |
+
|
| 3 |
+
macro_rules! def_pub_const {
|
| 4 |
+
($name:ident, $value:expr) => {
|
| 5 |
+
pub const $name: &'static str = $value;
|
| 6 |
+
};
|
| 7 |
+
}
|
| 8 |
+
def_pub_const!(ERR_UNSUPPORTED_GIF, "不支持动态 GIF");
|
| 9 |
+
def_pub_const!(
|
| 10 |
+
ERR_UNSUPPORTED_IMAGE_FORMAT,
|
| 11 |
+
"不支持的图片格式,仅支持 PNG、JPEG、WEBP 和非动态 GIF"
|
| 12 |
+
);
|
| 13 |
+
def_pub_const!(ERR_NODATA, "No data");
|
| 14 |
+
|
| 15 |
+
const MODEL_OBJECT: &str = "model";
|
| 16 |
+
const CREATED: &i64 = &1706659200;
|
| 17 |
+
|
| 18 |
+
def_pub_const!(ANTHROPIC, "anthropic");
|
| 19 |
+
def_pub_const!(CURSOR, "cursor");
|
| 20 |
+
def_pub_const!(GOOGLE, "google");
|
| 21 |
+
def_pub_const!(OPENAI, "openai");
|
| 22 |
+
def_pub_const!(DEEPSEEK, "deepseek");
|
| 23 |
+
|
| 24 |
+
def_pub_const!(CLAUDE_3_5_SONNET, "claude-3.5-sonnet");
|
| 25 |
+
def_pub_const!(GPT_4, "gpt-4");
|
| 26 |
+
def_pub_const!(GPT_4O, "gpt-4o");
|
| 27 |
+
def_pub_const!(CLAUDE_3_OPUS, "claude-3-opus");
|
| 28 |
+
def_pub_const!(CURSOR_FAST, "cursor-fast");
|
| 29 |
+
def_pub_const!(CURSOR_SMALL, "cursor-small");
|
| 30 |
+
def_pub_const!(GPT_3_5_TURBO, "gpt-3.5-turbo");
|
| 31 |
+
def_pub_const!(GPT_4_TURBO_2024_04_09, "gpt-4-turbo-2024-04-09");
|
| 32 |
+
def_pub_const!(GPT_4O_128K, "gpt-4o-128k");
|
| 33 |
+
def_pub_const!(GEMINI_1_5_FLASH_500K, "gemini-1.5-flash-500k");
|
| 34 |
+
def_pub_const!(CLAUDE_3_HAIKU_200K, "claude-3-haiku-200k");
|
| 35 |
+
def_pub_const!(CLAUDE_3_5_SONNET_200K, "claude-3-5-sonnet-200k");
|
| 36 |
+
def_pub_const!(CLAUDE_3_5_SONNET_20241022, "claude-3-5-sonnet-20241022");
|
| 37 |
+
def_pub_const!(GPT_4O_MINI, "gpt-4o-mini");
|
| 38 |
+
def_pub_const!(O1_MINI, "o1-mini");
|
| 39 |
+
def_pub_const!(O1_PREVIEW, "o1-preview");
|
| 40 |
+
def_pub_const!(O1, "o1");
|
| 41 |
+
def_pub_const!(CLAUDE_3_5_HAIKU, "claude-3.5-haiku");
|
| 42 |
+
def_pub_const!(GEMINI_EXP_1206, "gemini-exp-1206");
|
| 43 |
+
def_pub_const!(
|
| 44 |
+
GEMINI_2_0_FLASH_THINKING_EXP,
|
| 45 |
+
"gemini-2.0-flash-thinking-exp"
|
| 46 |
+
);
|
| 47 |
+
def_pub_const!(GEMINI_2_0_FLASH_EXP, "gemini-2.0-flash-exp");
|
| 48 |
+
def_pub_const!(DEEPSEEK_V3, "deepseek-v3");
|
| 49 |
+
def_pub_const!(DEEPSEEK_R1, "deepseek-r1");
|
| 50 |
+
|
| 51 |
+
// #[derive(Clone, PartialEq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
|
| 52 |
+
// pub enum ModelType {
|
| 53 |
+
// Claude35Sonnet,
|
| 54 |
+
// Gpt4,
|
| 55 |
+
// Gpt4o,
|
| 56 |
+
// Claude3Opus,
|
| 57 |
+
// CursorFast,
|
| 58 |
+
// CursorSmall,
|
| 59 |
+
// Gpt35Turbo,
|
| 60 |
+
// Gpt4Turbo202404,
|
| 61 |
+
// Gpt4o128k,
|
| 62 |
+
// Gemini15Flash500k,
|
| 63 |
+
// Claude3Haiku200k,
|
| 64 |
+
// Claude35Sonnet200k,
|
| 65 |
+
// Claude35Sonnet20241022,
|
| 66 |
+
// Gpt4oMini,
|
| 67 |
+
// O1Mini,
|
| 68 |
+
// O1Preview,
|
| 69 |
+
// O1,
|
| 70 |
+
// Claude35Haiku,
|
| 71 |
+
// GeminiExp1206,
|
| 72 |
+
// Gemini20FlashThinkingExp,
|
| 73 |
+
// Gemini20FlashExp,
|
| 74 |
+
// DeepseekV3,
|
| 75 |
+
// DeepseekR1,
|
| 76 |
+
// }
|
| 77 |
+
|
| 78 |
+
macro_rules! create_model {
|
| 79 |
+
($($id:expr, $owner:expr),* $(,)?) => {
|
| 80 |
+
pub const AVAILABLE_MODELS: [Model; count!($( ($id, $owner) )*)] = [
|
| 81 |
+
$(
|
| 82 |
+
Model {
|
| 83 |
+
id: $id,
|
| 84 |
+
created: CREATED,
|
| 85 |
+
object: MODEL_OBJECT,
|
| 86 |
+
owned_by: $owner,
|
| 87 |
+
},
|
| 88 |
+
)*
|
| 89 |
+
];
|
| 90 |
+
};
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
macro_rules! count {
|
| 94 |
+
() => (0);
|
| 95 |
+
(($id:expr, $owner:expr) $( ($id2:expr, $owner2:expr) )*) => (1 + count!($( ($id2, $owner2) )*));
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// impl ModelType {
|
| 99 |
+
// pub fn as_str_name(&self) -> &'static str {
|
| 100 |
+
// match self {
|
| 101 |
+
// ModelType::Claude35Sonnet => CLAUDE_3_5_SONNET,
|
| 102 |
+
// ModelType::Gpt4 => GPT_4,
|
| 103 |
+
// ModelType::Gpt4o => GPT_4O,
|
| 104 |
+
// ModelType::Claude3Opus => CLAUDE_3_OPUS,
|
| 105 |
+
// ModelType::CursorFast => CURSOR_FAST,
|
| 106 |
+
// ModelType::CursorSmall => CURSOR_SMALL,
|
| 107 |
+
// ModelType::Gpt35Turbo => GPT_3_5_TURBO,
|
| 108 |
+
// ModelType::Gpt4Turbo202404 => GPT_4_TURBO_2024_04_09,
|
| 109 |
+
// ModelType::Gpt4o128k => GPT_4O_128K,
|
| 110 |
+
// ModelType::Gemini15Flash500k => GEMINI_1_5_FLASH_500K,
|
| 111 |
+
// ModelType::Claude3Haiku200k => CLAUDE_3_HAIKU_200K,
|
| 112 |
+
// ModelType::Claude35Sonnet200k => CLAUDE_3_5_SONNET_200K,
|
| 113 |
+
// ModelType::Claude35Sonnet20241022 => CLAUDE_3_5_SONNET_20241022,
|
| 114 |
+
// ModelType::Gpt4oMini => GPT_4O_MINI,
|
| 115 |
+
// ModelType::O1Mini => O1_MINI,
|
| 116 |
+
// ModelType::O1Preview => O1_PREVIEW,
|
| 117 |
+
// ModelType::O1 => O1,
|
| 118 |
+
// ModelType::Claude35Haiku => CLAUDE_3_5_HAIKU,
|
| 119 |
+
// ModelType::GeminiExp1206 => GEMINI_EXP_1206,
|
| 120 |
+
// ModelType::Gemini20FlashThinkingExp => GEMINI_2_0_FLASH_THINKING_EXP,
|
| 121 |
+
// ModelType::Gemini20FlashExp => GEMINI_2_0_FLASH_EXP,
|
| 122 |
+
// ModelType::DeepseekV3 => DEEPSEEK_V3,
|
| 123 |
+
// ModelType::DeepseekR1 => DEEPSEEK_R1,
|
| 124 |
+
// }
|
| 125 |
+
// }
|
| 126 |
+
|
| 127 |
+
// pub fn from_str_name(id :&str) -> Option<ModelType> {
|
| 128 |
+
// match id {
|
| 129 |
+
// CLAUDE_3_5_SONNET => Some(ModelType::Claude35Sonnet),
|
| 130 |
+
// GPT_4 => Some(ModelType::Gpt4),
|
| 131 |
+
// GPT_4O => Some(ModelType::Gpt4o),
|
| 132 |
+
// CLAUDE_3_OPUS => Some(ModelType::Claude3Opus),
|
| 133 |
+
// CURSOR_FAST => Some(ModelType::CursorFast),
|
| 134 |
+
// CURSOR_SMALL => Some(ModelType::CursorSmall),
|
| 135 |
+
// GPT_3_5_TURBO => Some(ModelType::Gpt35Turbo),
|
| 136 |
+
// GPT_4_TURBO_2024_04_09 => Some(ModelType::Gpt4Turbo202404),
|
| 137 |
+
// GPT_4O_128K => Some(ModelType::Gpt4o128k),
|
| 138 |
+
// GEMINI_1_5_FLASH_500K => Some(ModelType::Gemini15Flash500k),
|
| 139 |
+
// CLAUDE_3_HAIKU_200K => Some(ModelType::Claude3Haiku200k),
|
| 140 |
+
// CLAUDE_3_5_SONNET_200K => Some(ModelType::Claude35Sonnet200k),
|
| 141 |
+
// CLAUDE_3_5_SONNET_20241022 => Some(ModelType::Claude35Sonnet20241022),
|
| 142 |
+
// GPT_4O_MINI => Some(ModelType::Gpt4oMini),
|
| 143 |
+
// O1_MINI => Some(ModelType::O1Mini),
|
| 144 |
+
// O1_PREVIEW => Some(ModelType::O1Preview),
|
| 145 |
+
// O1 => Some(ModelType::O1),
|
| 146 |
+
// CLAUDE_3_5_HAIKU => Some(ModelType::Claude35Haiku),
|
| 147 |
+
// GEMINI_EXP_1206 => Some(ModelType::GeminiExp1206),
|
| 148 |
+
// GEMINI_2_0_FLASH_THINKING_EXP => Some(ModelType::Gemini20FlashThinkingExp),
|
| 149 |
+
// GEMINI_2_0_FLASH_EXP => Some(ModelType::Gemini20FlashExp),
|
| 150 |
+
// DEEPSEEK_V3 => Some(ModelType::DeepseekV3),
|
| 151 |
+
// DEEPSEEK_R1 => Some(ModelType::DeepseekR1),
|
| 152 |
+
// _ => None,
|
| 153 |
+
// }
|
| 154 |
+
// }
|
| 155 |
+
// }
|
| 156 |
+
|
| 157 |
+
create_model!(
|
| 158 |
+
CLAUDE_3_5_SONNET, ANTHROPIC,
|
| 159 |
+
GPT_4, OPENAI,
|
| 160 |
+
GPT_4O, OPENAI,
|
| 161 |
+
CLAUDE_3_OPUS, ANTHROPIC,
|
| 162 |
+
CURSOR_FAST, CURSOR,
|
| 163 |
+
CURSOR_SMALL, CURSOR,
|
| 164 |
+
GPT_3_5_TURBO, OPENAI,
|
| 165 |
+
GPT_4_TURBO_2024_04_09, OPENAI,
|
| 166 |
+
GPT_4O_128K, OPENAI,
|
| 167 |
+
GEMINI_1_5_FLASH_500K, GOOGLE,
|
| 168 |
+
CLAUDE_3_HAIKU_200K, ANTHROPIC,
|
| 169 |
+
CLAUDE_3_5_SONNET_200K, ANTHROPIC,
|
| 170 |
+
CLAUDE_3_5_SONNET_20241022, ANTHROPIC,
|
| 171 |
+
GPT_4O_MINI, OPENAI,
|
| 172 |
+
O1_MINI, OPENAI,
|
| 173 |
+
O1_PREVIEW, OPENAI,
|
| 174 |
+
O1, OPENAI,
|
| 175 |
+
CLAUDE_3_5_HAIKU, ANTHROPIC,
|
| 176 |
+
GEMINI_EXP_1206, GOOGLE,
|
| 177 |
+
GEMINI_2_0_FLASH_THINKING_EXP, GOOGLE,
|
| 178 |
+
GEMINI_2_0_FLASH_EXP, GOOGLE,
|
| 179 |
+
DEEPSEEK_V3, DEEPSEEK,
|
| 180 |
+
DEEPSEEK_R1, DEEPSEEK,
|
| 181 |
+
);
|
| 182 |
+
|
| 183 |
+
pub const USAGE_CHECK_MODELS: [&str; 11] = [
|
| 184 |
+
CLAUDE_3_5_SONNET_20241022,
|
| 185 |
+
CLAUDE_3_5_SONNET,
|
| 186 |
+
GEMINI_EXP_1206,
|
| 187 |
+
GPT_4,
|
| 188 |
+
GPT_4_TURBO_2024_04_09,
|
| 189 |
+
GPT_4O,
|
| 190 |
+
CLAUDE_3_5_HAIKU,
|
| 191 |
+
GPT_4O_128K,
|
| 192 |
+
GEMINI_1_5_FLASH_500K,
|
| 193 |
+
CLAUDE_3_HAIKU_200K,
|
| 194 |
+
CLAUDE_3_5_SONNET_200K,
|
| 195 |
+
];
|
| 196 |
+
|
| 197 |
+
pub const LONG_CONTEXT_MODELS: [&str; 4] = [
|
| 198 |
+
GPT_4O_128K,
|
| 199 |
+
GEMINI_1_5_FLASH_500K,
|
| 200 |
+
CLAUDE_3_HAIKU_200K,
|
| 201 |
+
CLAUDE_3_5_SONNET_200K,
|
| 202 |
+
];
|
| 203 |
+
|
| 204 |
+
// include!("constant/models.rs");
|
src/chat/constant/models.rs
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pub struct DefaultModel {
|
| 2 |
+
pub default_on: bool,
|
| 3 |
+
pub is_long_context_only: Option<bool>,
|
| 4 |
+
pub name: &'static str,
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
pub const AVAILABLE_MODELS2: [DefaultModel; 22] = [
|
| 8 |
+
DefaultModel {
|
| 9 |
+
default_on: true,
|
| 10 |
+
is_long_context_only: Some(false),
|
| 11 |
+
name: CLAUDE_3_5_SONNET,
|
| 12 |
+
},
|
| 13 |
+
DefaultModel {
|
| 14 |
+
default_on: false,
|
| 15 |
+
is_long_context_only: None,
|
| 16 |
+
name: GPT_4,
|
| 17 |
+
},
|
| 18 |
+
DefaultModel {
|
| 19 |
+
default_on: true,
|
| 20 |
+
is_long_context_only: None,
|
| 21 |
+
name: GPT_4O,
|
| 22 |
+
},
|
| 23 |
+
DefaultModel {
|
| 24 |
+
default_on: false,
|
| 25 |
+
is_long_context_only: None,
|
| 26 |
+
name: CLAUDE_3_OPUS,
|
| 27 |
+
},
|
| 28 |
+
DefaultModel {
|
| 29 |
+
default_on: false,
|
| 30 |
+
is_long_context_only: None,
|
| 31 |
+
name: CURSOR_FAST,
|
| 32 |
+
},
|
| 33 |
+
DefaultModel {
|
| 34 |
+
default_on: false,
|
| 35 |
+
is_long_context_only: None,
|
| 36 |
+
name: CURSOR_SMALL,
|
| 37 |
+
},
|
| 38 |
+
DefaultModel {
|
| 39 |
+
default_on: false,
|
| 40 |
+
is_long_context_only: None,
|
| 41 |
+
name: GPT_3_5_TURBO,
|
| 42 |
+
},
|
| 43 |
+
DefaultModel {
|
| 44 |
+
default_on: false,
|
| 45 |
+
is_long_context_only: None,
|
| 46 |
+
name: GPT_4_TURBO_2024_04_09,
|
| 47 |
+
},
|
| 48 |
+
DefaultModel {
|
| 49 |
+
default_on: true,
|
| 50 |
+
is_long_context_only: Some(true),
|
| 51 |
+
name: GPT_4O_128K,
|
| 52 |
+
},
|
| 53 |
+
DefaultModel {
|
| 54 |
+
default_on: true,
|
| 55 |
+
is_long_context_only: Some(true),
|
| 56 |
+
name: GEMINI_1_5_FLASH_500K,
|
| 57 |
+
},
|
| 58 |
+
DefaultModel {
|
| 59 |
+
default_on: true,
|
| 60 |
+
is_long_context_only: Some(true),
|
| 61 |
+
name: CLAUDE_3_HAIKU_200K,
|
| 62 |
+
},
|
| 63 |
+
DefaultModel {
|
| 64 |
+
default_on: true,
|
| 65 |
+
is_long_context_only: Some(true),
|
| 66 |
+
name: CLAUDE_3_5_SONNET_200K,
|
| 67 |
+
},
|
| 68 |
+
DefaultModel {
|
| 69 |
+
default_on: false,
|
| 70 |
+
is_long_context_only: Some(false),
|
| 71 |
+
name: CLAUDE_3_5_SONNET_20241022,
|
| 72 |
+
},
|
| 73 |
+
DefaultModel {
|
| 74 |
+
default_on: true,
|
| 75 |
+
is_long_context_only: Some(false),
|
| 76 |
+
name: GPT_4O_MINI,
|
| 77 |
+
},
|
| 78 |
+
DefaultModel {
|
| 79 |
+
default_on: true,
|
| 80 |
+
is_long_context_only: Some(false),
|
| 81 |
+
name: O1_MINI,
|
| 82 |
+
},
|
| 83 |
+
DefaultModel {
|
| 84 |
+
default_on: true,
|
| 85 |
+
is_long_context_only: Some(false),
|
| 86 |
+
name: O1_PREVIEW,
|
| 87 |
+
},
|
| 88 |
+
DefaultModel {
|
| 89 |
+
default_on: true,
|
| 90 |
+
is_long_context_only: Some(false),
|
| 91 |
+
name: O1,
|
| 92 |
+
},
|
| 93 |
+
DefaultModel {
|
| 94 |
+
default_on: false,
|
| 95 |
+
is_long_context_only: Some(false),
|
| 96 |
+
name: CLAUDE_3_5_HAIKU,
|
| 97 |
+
},
|
| 98 |
+
DefaultModel {
|
| 99 |
+
default_on: false,
|
| 100 |
+
is_long_context_only: None,
|
| 101 |
+
name: GEMINI_EXP_1206,
|
| 102 |
+
},
|
| 103 |
+
DefaultModel {
|
| 104 |
+
default_on: false,
|
| 105 |
+
is_long_context_only: None,
|
| 106 |
+
name: GEMINI_2_0_FLASH_THINKING_EXP,
|
| 107 |
+
},
|
| 108 |
+
DefaultModel {
|
| 109 |
+
default_on: false,
|
| 110 |
+
is_long_context_only: None,
|
| 111 |
+
name: GEMINI_2_0_FLASH_EXP,
|
| 112 |
+
},
|
| 113 |
+
DefaultModel {
|
| 114 |
+
default_on: false,
|
| 115 |
+
is_long_context_only: None,
|
| 116 |
+
name: DEEPSEEK_V3,
|
| 117 |
+
},
|
| 118 |
+
];
|
src/chat/error.rs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use super::aiserver::v1::ErrorDetails;
|
| 2 |
+
use crate::common::model::{ApiStatus, ErrorResponse as CommonErrorResponse};
|
| 3 |
+
use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
|
| 4 |
+
use prost::Message as _;
|
| 5 |
+
use reqwest::StatusCode;
|
| 6 |
+
use serde::{Deserialize, Serialize};
|
| 7 |
+
|
| 8 |
+
#[derive(Deserialize)]
|
| 9 |
+
pub struct ChatError {
|
| 10 |
+
error: ErrorBody,
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
#[derive(Deserialize)]
|
| 14 |
+
pub struct ErrorBody {
|
| 15 |
+
code: String,
|
| 16 |
+
// message: String, always: Error
|
| 17 |
+
details: Vec<ErrorDetail>,
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
#[derive(Deserialize)]
|
| 21 |
+
pub struct ErrorDetail {
|
| 22 |
+
// #[serde(rename = "type")]
|
| 23 |
+
// error_type: String, always: aiserver.v1.ErrorDetails
|
| 24 |
+
// debug: ErrorDebug,
|
| 25 |
+
value: String,
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// #[derive(Deserialize)]
|
| 29 |
+
// pub struct ErrorDebug {
|
| 30 |
+
// error: String,
|
| 31 |
+
// details: ErrorDetails,
|
| 32 |
+
// // #[serde(rename = "isExpected")]
|
| 33 |
+
// // is_expected: Option<bool>,
|
| 34 |
+
// }
|
| 35 |
+
|
| 36 |
+
// #[derive(Deserialize)]
|
| 37 |
+
// pub struct ErrorDetails {
|
| 38 |
+
// title: String,
|
| 39 |
+
// detail: String,
|
| 40 |
+
// // #[serde(rename = "isRetryable")]
|
| 41 |
+
// // is_retryable: Option<bool>,
|
| 42 |
+
// }
|
| 43 |
+
|
| 44 |
+
impl ChatError {
|
| 45 |
+
pub fn to_error_response(self) -> ErrorResponse {
|
| 46 |
+
if self.error.details.is_empty() {
|
| 47 |
+
return ErrorResponse {
|
| 48 |
+
status: 500,
|
| 49 |
+
code: "unknown".to_string(),
|
| 50 |
+
error: None,
|
| 51 |
+
};
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
let error_details = self.error.details.first().and_then(|detail| {
|
| 55 |
+
STANDARD_NO_PAD
|
| 56 |
+
.decode(&detail.value)
|
| 57 |
+
.ok()
|
| 58 |
+
.map(bytes::Bytes::from)
|
| 59 |
+
.and_then(|buf| ErrorDetails::decode(buf).ok())
|
| 60 |
+
});
|
| 61 |
+
|
| 62 |
+
let status = error_details
|
| 63 |
+
.as_ref()
|
| 64 |
+
.map(|details| details.status_code())
|
| 65 |
+
.unwrap_or(500);
|
| 66 |
+
|
| 67 |
+
ErrorResponse {
|
| 68 |
+
status,
|
| 69 |
+
code: self.error.code,
|
| 70 |
+
error: error_details
|
| 71 |
+
.and_then(|details| details.details)
|
| 72 |
+
.map(|custom_details| Error {
|
| 73 |
+
message: custom_details.title,
|
| 74 |
+
details: custom_details.detail,
|
| 75 |
+
}),
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
#[derive(Serialize)]
|
| 81 |
+
pub struct ErrorResponse {
|
| 82 |
+
pub status: u16,
|
| 83 |
+
pub code: String,
|
| 84 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 85 |
+
pub error: Option<Error>,
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
#[derive(Serialize)]
|
| 89 |
+
pub struct Error {
|
| 90 |
+
pub message: String,
|
| 91 |
+
pub details: String,
|
| 92 |
+
// pub value: String,
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
impl ErrorResponse {
|
| 96 |
+
// pub fn to_json(&self) -> serde_json::Value {
|
| 97 |
+
// serde_json::to_value(self).unwrap()
|
| 98 |
+
// }
|
| 99 |
+
|
| 100 |
+
pub fn status_code(&self) -> StatusCode {
|
| 101 |
+
StatusCode::from_u16(self.status).unwrap()
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
pub fn native_code(&self) -> String {
|
| 105 |
+
self.error.as_ref().map_or_else(
|
| 106 |
+
|| self.code.replace("_", " "),
|
| 107 |
+
|error| error.message.clone(),
|
| 108 |
+
)
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
pub fn to_common(self) -> CommonErrorResponse {
|
| 112 |
+
CommonErrorResponse {
|
| 113 |
+
status: ApiStatus::Error,
|
| 114 |
+
code: Some(self.status),
|
| 115 |
+
error: self
|
| 116 |
+
.error
|
| 117 |
+
.as_ref()
|
| 118 |
+
.map(|error| error.message.clone())
|
| 119 |
+
.or(Some(self.code.clone())),
|
| 120 |
+
message: self.error.as_ref().map(|error| error.details.clone()),
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
pub enum StreamError {
|
| 126 |
+
ChatError(ChatError),
|
| 127 |
+
DataLengthLessThan5,
|
| 128 |
+
EmptyStream,
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
impl std::fmt::Display for StreamError {
|
| 132 |
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
| 133 |
+
match self {
|
| 134 |
+
StreamError::ChatError(error) => write!(f, "{}", error.error.code),
|
| 135 |
+
StreamError::DataLengthLessThan5 => write!(f, "data length less than 5"),
|
| 136 |
+
StreamError::EmptyStream => write!(f, "empty stream"),
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
}
|
src/chat/middleware.rs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mod auth;
|
| 2 |
+
pub use auth::*;
|
src/chat/middleware/auth.rs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::app::{constant::AUTHORIZATION_BEARER_PREFIX, lazy::AUTH_TOKEN};
|
| 2 |
+
use axum::{
|
| 3 |
+
body::Body,
|
| 4 |
+
http::{header::AUTHORIZATION, Request, StatusCode},
|
| 5 |
+
middleware::Next,
|
| 6 |
+
response::Response,
|
| 7 |
+
};
|
| 8 |
+
|
| 9 |
+
// 认证中间件函数
|
| 10 |
+
pub async fn auth_middleware(request: Request<Body>, next: Next) -> Result<Response, StatusCode> {
|
| 11 |
+
let auth_header = request
|
| 12 |
+
.headers()
|
| 13 |
+
.get(AUTHORIZATION)
|
| 14 |
+
.and_then(|h| h.to_str().ok())
|
| 15 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 16 |
+
.ok_or(StatusCode::UNAUTHORIZED)?;
|
| 17 |
+
|
| 18 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 19 |
+
return Err(StatusCode::UNAUTHORIZED);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
Ok(next.run(request).await)
|
| 23 |
+
}
|
src/chat/model.rs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use serde::{Deserialize, Serialize};
|
| 2 |
+
|
| 3 |
+
#[derive(Serialize, Deserialize)]
|
| 4 |
+
#[serde(untagged)]
|
| 5 |
+
pub enum MessageContent {
|
| 6 |
+
Text(String),
|
| 7 |
+
Vision(Vec<VisionMessageContent>),
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
#[derive(Serialize, Deserialize)]
|
| 11 |
+
pub struct VisionMessageContent {
|
| 12 |
+
#[serde(rename = "type")]
|
| 13 |
+
pub content_type: String,
|
| 14 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 15 |
+
pub text: Option<String>,
|
| 16 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 17 |
+
pub image_url: Option<ImageUrl>,
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
#[derive(Serialize, Deserialize)]
|
| 21 |
+
pub struct ImageUrl {
|
| 22 |
+
pub url: String,
|
| 23 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 24 |
+
pub detail: Option<String>,
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
#[derive(Serialize, Deserialize)]
|
| 28 |
+
pub struct Message {
|
| 29 |
+
pub role: Role,
|
| 30 |
+
pub content: MessageContent,
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
#[derive(Serialize, Deserialize, PartialEq)]
|
| 34 |
+
pub enum Role {
|
| 35 |
+
#[serde(rename = "system", alias = "developer")]
|
| 36 |
+
System,
|
| 37 |
+
#[serde(rename = "user", alias = "human")]
|
| 38 |
+
User,
|
| 39 |
+
#[serde(rename = "assistant", alias = "ai")]
|
| 40 |
+
Assistant,
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
#[derive(Serialize)]
|
| 44 |
+
pub struct ChatResponse {
|
| 45 |
+
pub id: String,
|
| 46 |
+
pub object: String,
|
| 47 |
+
pub created: i64,
|
| 48 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 49 |
+
pub model: Option<String>,
|
| 50 |
+
pub choices: Vec<Choice>,
|
| 51 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 52 |
+
pub usage: Option<Usage>,
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
#[derive(Serialize)]
|
| 56 |
+
pub struct Choice {
|
| 57 |
+
pub index: i32,
|
| 58 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 59 |
+
pub message: Option<Message>,
|
| 60 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 61 |
+
pub delta: Option<Delta>,
|
| 62 |
+
pub finish_reason: Option<String>,
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
#[derive(Serialize)]
|
| 66 |
+
pub struct Delta {
|
| 67 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 68 |
+
pub role: Option<Role>,
|
| 69 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 70 |
+
pub content: Option<String>,
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
#[derive(Serialize)]
|
| 74 |
+
pub struct Usage {
|
| 75 |
+
pub prompt_tokens: u32,
|
| 76 |
+
pub completion_tokens: u32,
|
| 77 |
+
pub total_tokens: u32,
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
// 模型定义
|
| 81 |
+
#[derive(Serialize, Clone)]
|
| 82 |
+
pub struct Model {
|
| 83 |
+
pub id: &'static str,
|
| 84 |
+
pub created: &'static i64,
|
| 85 |
+
pub object: &'static str,
|
| 86 |
+
pub owned_by: &'static str,
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
use super::constant::USAGE_CHECK_MODELS;
|
| 90 |
+
use crate::app::model::{AppConfig, UsageCheck};
|
| 91 |
+
|
| 92 |
+
impl Model {
|
| 93 |
+
pub fn is_usage_check(&self, usage_check: Option<UsageCheck>) -> bool {
|
| 94 |
+
match usage_check.unwrap_or(AppConfig::get_usage_check()) {
|
| 95 |
+
UsageCheck::None => false,
|
| 96 |
+
UsageCheck::Default => USAGE_CHECK_MODELS.contains(&self.id),
|
| 97 |
+
UsageCheck::All => true,
|
| 98 |
+
UsageCheck::Custom(models) => models.contains(&self.id),
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
#[derive(Serialize)]
|
| 104 |
+
pub struct ModelsResponse {
|
| 105 |
+
pub object: &'static str,
|
| 106 |
+
pub data: &'static [Model],
|
| 107 |
+
}
|
src/chat/route.rs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mod logs;
|
| 2 |
+
pub use logs::{handle_logs, handle_logs_post};
|
| 3 |
+
mod health;
|
| 4 |
+
pub use health::{handle_health, handle_root};
|
| 5 |
+
mod tokens;
|
| 6 |
+
pub use tokens::{
|
| 7 |
+
handle_add_tokens, handle_basic_calibration, handle_delete_tokens, handle_get_checksum,
|
| 8 |
+
handle_get_hash, handle_get_timestamp_header, handle_get_tokens, handle_reload_tokens,
|
| 9 |
+
handle_tokens_page, handle_update_tokens,
|
| 10 |
+
};
|
| 11 |
+
mod profile;
|
| 12 |
+
pub use profile::handle_user_info;
|
| 13 |
+
mod config;
|
| 14 |
+
pub use config::{
|
| 15 |
+
handle_about, handle_build_key, handle_build_key_page, handle_config_page, handle_env_example,
|
| 16 |
+
handle_readme, handle_static,
|
| 17 |
+
};
|
| 18 |
+
mod api;
|
| 19 |
+
pub use api::handle_api_page;
|
src/chat/route/api.rs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use axum::response::{IntoResponse, Response};
|
| 2 |
+
use reqwest::header::CONTENT_TYPE;
|
| 3 |
+
|
| 4 |
+
use crate::{
|
| 5 |
+
app::constant::{
|
| 6 |
+
CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_API_PATH,
|
| 7 |
+
},
|
| 8 |
+
AppConfig, PageContent,
|
| 9 |
+
};
|
| 10 |
+
|
| 11 |
+
pub async fn handle_api_page() -> impl IntoResponse {
|
| 12 |
+
match AppConfig::get_page_content(ROUTE_API_PATH).unwrap_or_default() {
|
| 13 |
+
PageContent::Default => Response::builder()
|
| 14 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 15 |
+
.body(include_str!("../../../static/api.min.html").to_string())
|
| 16 |
+
.unwrap(),
|
| 17 |
+
PageContent::Text(content) => Response::builder()
|
| 18 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 19 |
+
.body(content.clone())
|
| 20 |
+
.unwrap(),
|
| 21 |
+
PageContent::Html(content) => Response::builder()
|
| 22 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 23 |
+
.body(content.clone())
|
| 24 |
+
.unwrap(),
|
| 25 |
+
}
|
| 26 |
+
}
|
src/chat/route/config.rs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::{
|
| 3 |
+
constant::{
|
| 4 |
+
AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_CSS_WITH_UTF8, CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_JS_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_ABOUT_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH, ROUTE_README_PATH, ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH
|
| 5 |
+
},
|
| 6 |
+
lazy::{AUTH_TOKEN, KEY_PREFIX},
|
| 7 |
+
model::{AppConfig, BuildKeyRequest, BuildKeyResponse, PageContent, UsageCheckModelType},
|
| 8 |
+
},
|
| 9 |
+
chat::config::{key_config, KeyConfig},
|
| 10 |
+
common::utils::{to_base64, token_to_tokeninfo},
|
| 11 |
+
};
|
| 12 |
+
use axum::{
|
| 13 |
+
body::Body,
|
| 14 |
+
extract::Path,
|
| 15 |
+
http::{
|
| 16 |
+
header::{AUTHORIZATION, CONTENT_TYPE, LOCATION},
|
| 17 |
+
HeaderMap, StatusCode,
|
| 18 |
+
},
|
| 19 |
+
response::{IntoResponse, Response},
|
| 20 |
+
Json,
|
| 21 |
+
};
|
| 22 |
+
use prost::Message as _;
|
| 23 |
+
|
| 24 |
+
pub async fn handle_env_example() -> impl IntoResponse {
|
| 25 |
+
Response::builder()
|
| 26 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 27 |
+
.body(include_str!("../../../.env.example").to_string())
|
| 28 |
+
.unwrap()
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
// 配置页面处理函数
|
| 32 |
+
pub async fn handle_config_page() -> impl IntoResponse {
|
| 33 |
+
match AppConfig::get_page_content(ROUTE_CONFIG_PATH).unwrap_or_default() {
|
| 34 |
+
PageContent::Default => Response::builder()
|
| 35 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 36 |
+
.body(include_str!("../../../static/config.min.html").to_string())
|
| 37 |
+
.unwrap(),
|
| 38 |
+
PageContent::Text(content) => Response::builder()
|
| 39 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 40 |
+
.body(content.clone())
|
| 41 |
+
.unwrap(),
|
| 42 |
+
PageContent::Html(content) => Response::builder()
|
| 43 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 44 |
+
.body(content.clone())
|
| 45 |
+
.unwrap(),
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
pub async fn handle_static(Path(path): Path<String>) -> impl IntoResponse {
|
| 50 |
+
match path.as_str() {
|
| 51 |
+
"shared-styles.css" => {
|
| 52 |
+
match AppConfig::get_page_content(ROUTE_SHARED_STYLES_PATH).unwrap_or_default() {
|
| 53 |
+
PageContent::Default => Response::builder()
|
| 54 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_CSS_WITH_UTF8)
|
| 55 |
+
.body(include_str!("../../../static/shared-styles.min.css").to_string())
|
| 56 |
+
.unwrap(),
|
| 57 |
+
PageContent::Text(content) | PageContent::Html(content) => Response::builder()
|
| 58 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_CSS_WITH_UTF8)
|
| 59 |
+
.body(content.clone())
|
| 60 |
+
.unwrap(),
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
"shared.js" => {
|
| 64 |
+
match AppConfig::get_page_content(ROUTE_SHARED_JS_PATH).unwrap_or_default() {
|
| 65 |
+
PageContent::Default => Response::builder()
|
| 66 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_JS_WITH_UTF8)
|
| 67 |
+
.body(include_str!("../../../static/shared.min.js").to_string())
|
| 68 |
+
.unwrap(),
|
| 69 |
+
PageContent::Text(content) | PageContent::Html(content) => Response::builder()
|
| 70 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_JS_WITH_UTF8)
|
| 71 |
+
.body(content.clone())
|
| 72 |
+
.unwrap(),
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
_ => Response::builder()
|
| 76 |
+
.status(StatusCode::NOT_FOUND)
|
| 77 |
+
.body("Not found".to_string())
|
| 78 |
+
.unwrap(),
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
pub async fn handle_readme() -> impl IntoResponse {
|
| 83 |
+
match AppConfig::get_page_content(ROUTE_README_PATH).unwrap_or_default() {
|
| 84 |
+
PageContent::Default => Response::builder()
|
| 85 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 86 |
+
.body(include_str!("../../../static/readme.min.html").to_string())
|
| 87 |
+
.unwrap(),
|
| 88 |
+
PageContent::Text(content) => Response::builder()
|
| 89 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 90 |
+
.body(content.clone())
|
| 91 |
+
.unwrap(),
|
| 92 |
+
PageContent::Html(content) => Response::builder()
|
| 93 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 94 |
+
.body(content.clone())
|
| 95 |
+
.unwrap(),
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
pub async fn handle_about() -> impl IntoResponse {
|
| 100 |
+
match AppConfig::get_page_content(ROUTE_ABOUT_PATH).unwrap_or_default() {
|
| 101 |
+
PageContent::Default => Response::builder()
|
| 102 |
+
.status(StatusCode::TEMPORARY_REDIRECT)
|
| 103 |
+
.header(LOCATION, ROUTE_README_PATH)
|
| 104 |
+
.body(Body::empty())
|
| 105 |
+
.unwrap(),
|
| 106 |
+
PageContent::Text(content) => Response::builder()
|
| 107 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 108 |
+
.body(Body::from(content.clone()))
|
| 109 |
+
.unwrap(),
|
| 110 |
+
PageContent::Html(content) => Response::builder()
|
| 111 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 112 |
+
.body(Body::from(content.clone()))
|
| 113 |
+
.unwrap(),
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
pub async fn handle_build_key_page() -> impl IntoResponse {
|
| 118 |
+
match AppConfig::get_page_content(ROUTE_BUILD_KEY_PATH).unwrap_or_default() {
|
| 119 |
+
PageContent::Default => Response::builder()
|
| 120 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 121 |
+
.body(include_str!("../../../static/build_key.min.html").to_string())
|
| 122 |
+
.unwrap(),
|
| 123 |
+
PageContent::Text(content) => Response::builder()
|
| 124 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 125 |
+
.body(content.clone())
|
| 126 |
+
.unwrap(),
|
| 127 |
+
PageContent::Html(content) => Response::builder()
|
| 128 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 129 |
+
.body(content.clone())
|
| 130 |
+
.unwrap(),
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
pub async fn handle_build_key(
|
| 135 |
+
headers: HeaderMap,
|
| 136 |
+
Json(request): Json<BuildKeyRequest>,
|
| 137 |
+
) -> (StatusCode, Json<BuildKeyResponse>) {
|
| 138 |
+
// 验证认证令牌
|
| 139 |
+
if AppConfig::is_share() {
|
| 140 |
+
let auth_header = headers
|
| 141 |
+
.get(AUTHORIZATION)
|
| 142 |
+
.and_then(|h| h.to_str().ok())
|
| 143 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX));
|
| 144 |
+
|
| 145 |
+
if auth_header.map_or(true, |h| h != AppConfig::get_share_token().as_str() && h != AUTH_TOKEN.as_str()) {
|
| 146 |
+
return (
|
| 147 |
+
StatusCode::UNAUTHORIZED,
|
| 148 |
+
Json(BuildKeyResponse::Error("Unauthorized".to_owned())),
|
| 149 |
+
);
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
// 验证并解析 auth_token
|
| 154 |
+
let token_info = match token_to_tokeninfo(&request.auth_token) {
|
| 155 |
+
Some(info) => info,
|
| 156 |
+
None => {
|
| 157 |
+
return (
|
| 158 |
+
StatusCode::BAD_REQUEST,
|
| 159 |
+
Json(BuildKeyResponse::Error("Invalid auth token".to_owned())),
|
| 160 |
+
)
|
| 161 |
+
}
|
| 162 |
+
};
|
| 163 |
+
|
| 164 |
+
// 构建 proto 消息
|
| 165 |
+
let mut key_config = KeyConfig {
|
| 166 |
+
auth_token: Some(token_info),
|
| 167 |
+
disable_vision: request.disable_vision,
|
| 168 |
+
enable_slow_pool: request.enable_slow_pool,
|
| 169 |
+
usage_check_models: None,
|
| 170 |
+
include_web_references: request.include_web_references,
|
| 171 |
+
};
|
| 172 |
+
|
| 173 |
+
if let Some(usage_check_models) = request.usage_check_models {
|
| 174 |
+
let usage_check = key_config::UsageCheckModel {
|
| 175 |
+
r#type: match usage_check_models.model_type {
|
| 176 |
+
UsageCheckModelType::Default => {
|
| 177 |
+
key_config::usage_check_model::Type::Default as i32
|
| 178 |
+
}
|
| 179 |
+
UsageCheckModelType::Disabled => {
|
| 180 |
+
key_config::usage_check_model::Type::Disabled as i32
|
| 181 |
+
}
|
| 182 |
+
UsageCheckModelType::All => key_config::usage_check_model::Type::All as i32,
|
| 183 |
+
UsageCheckModelType::Custom => key_config::usage_check_model::Type::Custom as i32,
|
| 184 |
+
},
|
| 185 |
+
model_ids: if matches!(usage_check_models.model_type, UsageCheckModelType::Custom) {
|
| 186 |
+
usage_check_models
|
| 187 |
+
.model_ids
|
| 188 |
+
.iter()
|
| 189 |
+
.map(|s| s.to_string())
|
| 190 |
+
.collect()
|
| 191 |
+
} else {
|
| 192 |
+
Vec::new()
|
| 193 |
+
},
|
| 194 |
+
};
|
| 195 |
+
key_config.usage_check_models = Some(usage_check);
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
// 序列化
|
| 199 |
+
let encoded = key_config.encode_to_vec();
|
| 200 |
+
|
| 201 |
+
let key = format!("{}{}", *KEY_PREFIX, to_base64(&encoded));
|
| 202 |
+
|
| 203 |
+
(StatusCode::OK, Json(BuildKeyResponse::Key(key)))
|
| 204 |
+
}
|
src/chat/route/health.rs
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::{
|
| 3 |
+
constant::{
|
| 4 |
+
AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_HTML_WITH_UTF8,
|
| 5 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, PKG_VERSION, ROUTE_ABOUT_PATH, ROUTE_API_PATH,
|
| 6 |
+
ROUTE_BASIC_CALIBRATION_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH,
|
| 7 |
+
ROUTE_ENV_EXAMPLE_PATH, ROUTE_GET_CHECKSUM, ROUTE_GET_HASH, ROUTE_GET_TIMESTAMP_HEADER,
|
| 8 |
+
ROUTE_HEALTH_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH,
|
| 9 |
+
ROUTE_STATIC_PATH, ROUTE_TOKENS_ADD_PATH, ROUTE_TOKENS_DELETE_PATH,
|
| 10 |
+
ROUTE_TOKENS_GET_PATH, ROUTE_TOKENS_PATH, ROUTE_TOKENS_UPDATE_PATH,
|
| 11 |
+
ROUTE_USER_INFO_PATH,
|
| 12 |
+
},
|
| 13 |
+
lazy::{get_start_time, AUTH_TOKEN, ROUTE_CHAT_PATH, ROUTE_MODELS_PATH},
|
| 14 |
+
model::{AppConfig, AppState, PageContent},
|
| 15 |
+
},
|
| 16 |
+
chat::constant::AVAILABLE_MODELS,
|
| 17 |
+
common::model::{
|
| 18 |
+
health::{CpuInfo, HealthCheckResponse, MemoryInfo, SystemInfo, SystemStats},
|
| 19 |
+
ApiStatus,
|
| 20 |
+
},
|
| 21 |
+
};
|
| 22 |
+
use axum::{
|
| 23 |
+
body::Body,
|
| 24 |
+
extract::State,
|
| 25 |
+
http::{
|
| 26 |
+
header::{CONTENT_TYPE, LOCATION},
|
| 27 |
+
HeaderMap, StatusCode,
|
| 28 |
+
},
|
| 29 |
+
response::{IntoResponse, Response},
|
| 30 |
+
Json,
|
| 31 |
+
};
|
| 32 |
+
use chrono::Local;
|
| 33 |
+
use reqwest::header::AUTHORIZATION;
|
| 34 |
+
use std::sync::Arc;
|
| 35 |
+
use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System};
|
| 36 |
+
use tokio::sync::Mutex;
|
| 37 |
+
|
| 38 |
+
pub async fn handle_root() -> impl IntoResponse {
|
| 39 |
+
match AppConfig::get_page_content(ROUTE_ROOT_PATH).unwrap_or_default() {
|
| 40 |
+
PageContent::Default => Response::builder()
|
| 41 |
+
.status(StatusCode::TEMPORARY_REDIRECT)
|
| 42 |
+
.header(LOCATION, ROUTE_HEALTH_PATH)
|
| 43 |
+
.body(Body::empty())
|
| 44 |
+
.unwrap(),
|
| 45 |
+
PageContent::Text(content) => Response::builder()
|
| 46 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 47 |
+
.body(Body::from(content.clone()))
|
| 48 |
+
.unwrap(),
|
| 49 |
+
PageContent::Html(content) => Response::builder()
|
| 50 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 51 |
+
.body(Body::from(content.clone()))
|
| 52 |
+
.unwrap(),
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
pub async fn handle_health(
|
| 57 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 58 |
+
headers: HeaderMap,
|
| 59 |
+
) -> Json<HealthCheckResponse> {
|
| 60 |
+
let start_time = get_start_time();
|
| 61 |
+
let uptime = (Local::now() - start_time).num_seconds();
|
| 62 |
+
|
| 63 |
+
// 先检查 headers 是否包含有效的认证信息
|
| 64 |
+
let stats = if headers
|
| 65 |
+
.get(AUTHORIZATION)
|
| 66 |
+
.and_then(|h| h.to_str().ok())
|
| 67 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 68 |
+
.map_or(false, |token| token == AUTH_TOKEN.as_str())
|
| 69 |
+
{
|
| 70 |
+
// 只有在需要系统信息时才创建实例
|
| 71 |
+
let mut sys = System::new_with_specifics(
|
| 72 |
+
RefreshKind::nothing()
|
| 73 |
+
.with_memory(MemoryRefreshKind::everything())
|
| 74 |
+
.with_cpu(CpuRefreshKind::everything()),
|
| 75 |
+
);
|
| 76 |
+
|
| 77 |
+
std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
|
| 78 |
+
|
| 79 |
+
// 刷新 CPU 和内存信息
|
| 80 |
+
sys.refresh_memory();
|
| 81 |
+
sys.refresh_cpu_usage();
|
| 82 |
+
|
| 83 |
+
let pid = std::process::id() as usize;
|
| 84 |
+
let process = sys.process(pid.into());
|
| 85 |
+
|
| 86 |
+
// 获取内存信息
|
| 87 |
+
let memory = process.map(|p| p.memory()).unwrap_or(0);
|
| 88 |
+
|
| 89 |
+
// 获取 CPU 使用率
|
| 90 |
+
let cpu_usage = sys.global_cpu_usage();
|
| 91 |
+
|
| 92 |
+
let state = state.lock().await;
|
| 93 |
+
|
| 94 |
+
Some(SystemStats {
|
| 95 |
+
started: start_time.to_string(),
|
| 96 |
+
total_requests: state.total_requests,
|
| 97 |
+
active_requests: state.active_requests,
|
| 98 |
+
system: SystemInfo {
|
| 99 |
+
memory: MemoryInfo {
|
| 100 |
+
rss: memory, // 物理内存使用量(字节)
|
| 101 |
+
},
|
| 102 |
+
cpu: CpuInfo {
|
| 103 |
+
usage: cpu_usage, // CPU 使用率(百分比)
|
| 104 |
+
},
|
| 105 |
+
},
|
| 106 |
+
})
|
| 107 |
+
} else {
|
| 108 |
+
None
|
| 109 |
+
};
|
| 110 |
+
|
| 111 |
+
Json(HealthCheckResponse {
|
| 112 |
+
status: ApiStatus::Healthy,
|
| 113 |
+
version: PKG_VERSION,
|
| 114 |
+
uptime,
|
| 115 |
+
stats,
|
| 116 |
+
models: AVAILABLE_MODELS.iter().map(|m| m.id).collect::<Vec<_>>(),
|
| 117 |
+
endpoints: vec![
|
| 118 |
+
ROUTE_CHAT_PATH.as_str(),
|
| 119 |
+
ROUTE_MODELS_PATH.as_str(),
|
| 120 |
+
ROUTE_TOKENS_PATH,
|
| 121 |
+
ROUTE_TOKENS_GET_PATH,
|
| 122 |
+
ROUTE_TOKENS_UPDATE_PATH,
|
| 123 |
+
ROUTE_TOKENS_ADD_PATH,
|
| 124 |
+
ROUTE_TOKENS_DELETE_PATH,
|
| 125 |
+
ROUTE_LOGS_PATH,
|
| 126 |
+
ROUTE_ENV_EXAMPLE_PATH,
|
| 127 |
+
ROUTE_CONFIG_PATH,
|
| 128 |
+
ROUTE_STATIC_PATH,
|
| 129 |
+
ROUTE_ABOUT_PATH,
|
| 130 |
+
ROUTE_README_PATH,
|
| 131 |
+
ROUTE_API_PATH,
|
| 132 |
+
ROUTE_GET_HASH,
|
| 133 |
+
ROUTE_GET_CHECKSUM,
|
| 134 |
+
ROUTE_GET_TIMESTAMP_HEADER,
|
| 135 |
+
ROUTE_BASIC_CALIBRATION_PATH,
|
| 136 |
+
ROUTE_USER_INFO_PATH,
|
| 137 |
+
ROUTE_BUILD_KEY_PATH,
|
| 138 |
+
],
|
| 139 |
+
})
|
| 140 |
+
}
|
src/chat/route/logs.rs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::{
|
| 3 |
+
constant::{
|
| 4 |
+
AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_HTML_WITH_UTF8,
|
| 5 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_LOGS_PATH,
|
| 6 |
+
},
|
| 7 |
+
lazy::AUTH_TOKEN,
|
| 8 |
+
model::{AppConfig, AppState, PageContent, RequestLog},
|
| 9 |
+
},
|
| 10 |
+
common::{model::ApiStatus, utils::extract_token},
|
| 11 |
+
};
|
| 12 |
+
use axum::{
|
| 13 |
+
body::Body,
|
| 14 |
+
extract::State,
|
| 15 |
+
http::{
|
| 16 |
+
header::{AUTHORIZATION, CONTENT_TYPE},
|
| 17 |
+
HeaderMap, StatusCode,
|
| 18 |
+
},
|
| 19 |
+
response::{IntoResponse, Response},
|
| 20 |
+
Json,
|
| 21 |
+
};
|
| 22 |
+
use chrono::Local;
|
| 23 |
+
use std::sync::Arc;
|
| 24 |
+
use tokio::sync::Mutex;
|
| 25 |
+
|
| 26 |
+
// 日志处理
|
| 27 |
+
pub async fn handle_logs() -> impl IntoResponse {
|
| 28 |
+
match AppConfig::get_page_content(ROUTE_LOGS_PATH).unwrap_or_default() {
|
| 29 |
+
PageContent::Default => Response::builder()
|
| 30 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 31 |
+
.body(Body::from(
|
| 32 |
+
include_str!("../../../static/logs.min.html").to_string(),
|
| 33 |
+
))
|
| 34 |
+
.unwrap(),
|
| 35 |
+
PageContent::Text(content) => Response::builder()
|
| 36 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 37 |
+
.body(Body::from(content.clone()))
|
| 38 |
+
.unwrap(),
|
| 39 |
+
PageContent::Html(content) => Response::builder()
|
| 40 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 41 |
+
.body(Body::from(content.clone()))
|
| 42 |
+
.unwrap(),
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
pub async fn handle_logs_post(
|
| 47 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 48 |
+
headers: HeaderMap,
|
| 49 |
+
) -> Result<Json<LogsResponse>, StatusCode> {
|
| 50 |
+
let auth_token = AUTH_TOKEN.as_str();
|
| 51 |
+
|
| 52 |
+
// 获取认证头
|
| 53 |
+
let auth_header = headers
|
| 54 |
+
.get(AUTHORIZATION)
|
| 55 |
+
.and_then(|h| h.to_str().ok())
|
| 56 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 57 |
+
.ok_or(StatusCode::UNAUTHORIZED)?;
|
| 58 |
+
|
| 59 |
+
let state = state.lock().await;
|
| 60 |
+
|
| 61 |
+
// 如果是管理员token,返回所有日志
|
| 62 |
+
if auth_header == auth_token {
|
| 63 |
+
return Ok(Json(LogsResponse {
|
| 64 |
+
status: ApiStatus::Success,
|
| 65 |
+
total: state.total_requests,
|
| 66 |
+
active: Some(state.active_requests),
|
| 67 |
+
error: Some(state.error_requests),
|
| 68 |
+
logs: state.request_logs.clone(),
|
| 69 |
+
timestamp: Local::now().to_string(),
|
| 70 |
+
}));
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
// 解析 token
|
| 74 |
+
let token_part = extract_token(auth_header).ok_or(StatusCode::UNAUTHORIZED)?;
|
| 75 |
+
|
| 76 |
+
// 否则筛选出token匹配的日志
|
| 77 |
+
let filtered_logs: Vec<RequestLog> = state
|
| 78 |
+
.request_logs
|
| 79 |
+
.iter()
|
| 80 |
+
.filter(|log| log.token_info.token == token_part)
|
| 81 |
+
.cloned()
|
| 82 |
+
.collect();
|
| 83 |
+
|
| 84 |
+
// 如果没有匹配的日志,返回未授权错误
|
| 85 |
+
if filtered_logs.is_empty() {
|
| 86 |
+
return Err(StatusCode::UNAUTHORIZED);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
Ok(Json(LogsResponse {
|
| 90 |
+
status: ApiStatus::Success,
|
| 91 |
+
total: filtered_logs.len() as u64,
|
| 92 |
+
active: None,
|
| 93 |
+
error: None,
|
| 94 |
+
logs: filtered_logs,
|
| 95 |
+
timestamp: Local::now().to_string(),
|
| 96 |
+
}))
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
#[derive(serde::Serialize)]
|
| 100 |
+
pub struct LogsResponse {
|
| 101 |
+
pub status: ApiStatus,
|
| 102 |
+
pub total: u64,
|
| 103 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 104 |
+
pub active: Option<u64>,
|
| 105 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 106 |
+
pub error: Option<u64>,
|
| 107 |
+
pub logs: Vec<RequestLog>,
|
| 108 |
+
pub timestamp: String,
|
| 109 |
+
}
|
src/chat/route/profile.rs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
chat::constant::ERR_NODATA,
|
| 3 |
+
common::{model::userinfo::GetUserInfo, utils::{extract_token, get_token_profile}},
|
| 4 |
+
};
|
| 5 |
+
use axum::Json;
|
| 6 |
+
|
| 7 |
+
use super::tokens::TokenRequest;
|
| 8 |
+
|
| 9 |
+
pub async fn handle_user_info(Json(request): Json<TokenRequest>) -> Json<GetUserInfo> {
|
| 10 |
+
let auth_token = match request.token {
|
| 11 |
+
Some(token) => token,
|
| 12 |
+
None => {
|
| 13 |
+
return Json(GetUserInfo::Error {
|
| 14 |
+
error: ERR_NODATA.to_string(),
|
| 15 |
+
})
|
| 16 |
+
}
|
| 17 |
+
};
|
| 18 |
+
|
| 19 |
+
let token = match extract_token(&auth_token) {
|
| 20 |
+
Some(token) => token,
|
| 21 |
+
None => {
|
| 22 |
+
return Json(GetUserInfo::Error {
|
| 23 |
+
error: ERR_NODATA.to_string(),
|
| 24 |
+
})
|
| 25 |
+
}
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
match get_token_profile(&token).await {
|
| 29 |
+
Some(usage) => Json(GetUserInfo::Usage(usage)),
|
| 30 |
+
None => Json(GetUserInfo::Error {
|
| 31 |
+
error: ERR_NODATA.to_string(),
|
| 32 |
+
}),
|
| 33 |
+
}
|
| 34 |
+
}
|
src/chat/route/tokens.rs
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::{
|
| 3 |
+
constant::{
|
| 4 |
+
AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_HTML_WITH_UTF8,
|
| 5 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_TOKENS_PATH,
|
| 6 |
+
},
|
| 7 |
+
lazy::{AUTH_TOKEN, TOKEN_LIST_FILE},
|
| 8 |
+
model::{
|
| 9 |
+
AppConfig, AppState, PageContent, TokenAddRequestTokenInfo, TokenInfo,
|
| 10 |
+
TokenUpdateRequest, TokensDeleteRequest, TokensDeleteResponse,
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
common::{
|
| 14 |
+
model::{error::ChatError, ApiStatus, ErrorResponse},
|
| 15 |
+
utils::{
|
| 16 |
+
extract_time, extract_time_ks, extract_user_id, generate_checksum_with_default,
|
| 17 |
+
generate_checksum_with_repair, generate_hash, generate_timestamp_header, load_tokens,
|
| 18 |
+
parse_token, validate_token, validate_token_and_checksum, write_tokens,
|
| 19 |
+
},
|
| 20 |
+
},
|
| 21 |
+
};
|
| 22 |
+
use axum::{
|
| 23 |
+
extract::{Query, State},
|
| 24 |
+
http::{
|
| 25 |
+
header::{AUTHORIZATION, CONTENT_TYPE},
|
| 26 |
+
HeaderMap,
|
| 27 |
+
},
|
| 28 |
+
response::{IntoResponse, Response},
|
| 29 |
+
Json,
|
| 30 |
+
};
|
| 31 |
+
use reqwest::StatusCode;
|
| 32 |
+
use serde::{Deserialize, Serialize};
|
| 33 |
+
use std::sync::Arc;
|
| 34 |
+
use tokio::sync::Mutex;
|
| 35 |
+
|
| 36 |
+
pub async fn handle_get_hash() -> Response {
|
| 37 |
+
let hash = generate_hash();
|
| 38 |
+
|
| 39 |
+
let mut headers = HeaderMap::new();
|
| 40 |
+
headers.insert(
|
| 41 |
+
CONTENT_TYPE,
|
| 42 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(),
|
| 43 |
+
);
|
| 44 |
+
|
| 45 |
+
(headers, hash).into_response()
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
#[derive(Deserialize)]
|
| 49 |
+
pub struct ChecksumQuery {
|
| 50 |
+
#[serde(default)]
|
| 51 |
+
pub checksum: Option<String>,
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
pub async fn handle_get_checksum(Query(query): Query<ChecksumQuery>) -> Response {
|
| 55 |
+
let checksum = match query.checksum {
|
| 56 |
+
None => generate_checksum_with_default(),
|
| 57 |
+
Some(checksum) => generate_checksum_with_repair(&checksum),
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
let mut headers = HeaderMap::new();
|
| 61 |
+
headers.insert(
|
| 62 |
+
CONTENT_TYPE,
|
| 63 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(),
|
| 64 |
+
);
|
| 65 |
+
|
| 66 |
+
(headers, checksum).into_response()
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
pub async fn handle_get_timestamp_header() -> Response {
|
| 70 |
+
let timestamp_header = generate_timestamp_header();
|
| 71 |
+
|
| 72 |
+
let mut headers = HeaderMap::new();
|
| 73 |
+
headers.insert(
|
| 74 |
+
CONTENT_TYPE,
|
| 75 |
+
CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8.parse().unwrap(),
|
| 76 |
+
);
|
| 77 |
+
|
| 78 |
+
(headers, timestamp_header).into_response()
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
pub async fn handle_get_tokens(
|
| 82 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 83 |
+
headers: HeaderMap,
|
| 84 |
+
) -> Result<Json<TokenInfoResponse>, StatusCode> {
|
| 85 |
+
// 验证 AUTH_TOKEN
|
| 86 |
+
let auth_header = headers
|
| 87 |
+
.get(AUTHORIZATION)
|
| 88 |
+
.and_then(|h| h.to_str().ok())
|
| 89 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 90 |
+
.ok_or(StatusCode::UNAUTHORIZED)?;
|
| 91 |
+
|
| 92 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 93 |
+
return Err(StatusCode::UNAUTHORIZED);
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
let tokens = state.lock().await.token_infos.clone();
|
| 97 |
+
let tokens_count = tokens.len();
|
| 98 |
+
|
| 99 |
+
Ok(Json(TokenInfoResponse {
|
| 100 |
+
status: ApiStatus::Success,
|
| 101 |
+
tokens: Some(tokens),
|
| 102 |
+
tokens_count,
|
| 103 |
+
message: None,
|
| 104 |
+
}))
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
#[derive(Serialize)]
|
| 108 |
+
pub struct TokenInfoResponse {
|
| 109 |
+
pub status: ApiStatus,
|
| 110 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 111 |
+
pub tokens: Option<Vec<TokenInfo>>,
|
| 112 |
+
pub tokens_count: usize,
|
| 113 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 114 |
+
pub message: Option<String>,
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
pub async fn handle_reload_tokens(
|
| 118 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 119 |
+
headers: HeaderMap,
|
| 120 |
+
) -> Result<Json<TokenInfoResponse>, StatusCode> {
|
| 121 |
+
// 验证 AUTH_TOKEN
|
| 122 |
+
let auth_header = headers
|
| 123 |
+
.get(AUTHORIZATION)
|
| 124 |
+
.and_then(|h| h.to_str().ok())
|
| 125 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 126 |
+
.ok_or(StatusCode::UNAUTHORIZED)?;
|
| 127 |
+
|
| 128 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 129 |
+
return Err(StatusCode::UNAUTHORIZED);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
// 重新加载 tokens
|
| 133 |
+
let tokens = load_tokens();
|
| 134 |
+
let tokens_count = tokens.len();
|
| 135 |
+
|
| 136 |
+
// 更新应用状态
|
| 137 |
+
{
|
| 138 |
+
let mut state = state.lock().await;
|
| 139 |
+
state.token_infos = tokens;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
Ok(Json(TokenInfoResponse {
|
| 143 |
+
status: ApiStatus::Success,
|
| 144 |
+
tokens: None,
|
| 145 |
+
tokens_count,
|
| 146 |
+
message: Some("Token list has been reloaded".to_string()),
|
| 147 |
+
}))
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
pub async fn handle_update_tokens(
|
| 151 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 152 |
+
headers: HeaderMap,
|
| 153 |
+
Json(request): Json<TokenUpdateRequest>,
|
| 154 |
+
) -> Result<Json<TokenInfoResponse>, StatusCode> {
|
| 155 |
+
// 验证 AUTH_TOKEN
|
| 156 |
+
let auth_header = headers
|
| 157 |
+
.get(AUTHORIZATION)
|
| 158 |
+
.and_then(|h| h.to_str().ok())
|
| 159 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 160 |
+
.ok_or(StatusCode::UNAUTHORIZED)?;
|
| 161 |
+
|
| 162 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 163 |
+
return Err(StatusCode::UNAUTHORIZED);
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
let token_list_file = TOKEN_LIST_FILE.as_str();
|
| 167 |
+
|
| 168 |
+
std::fs::write(&token_list_file, &request.tokens)
|
| 169 |
+
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
| 170 |
+
|
| 171 |
+
// 重新加载 tokens
|
| 172 |
+
let token_infos = load_tokens();
|
| 173 |
+
let tokens_count = token_infos.len();
|
| 174 |
+
|
| 175 |
+
// 更新应用状态
|
| 176 |
+
{
|
| 177 |
+
let mut state = state.lock().await;
|
| 178 |
+
state.token_infos = token_infos;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
Ok(Json(TokenInfoResponse {
|
| 182 |
+
status: ApiStatus::Success,
|
| 183 |
+
tokens: None,
|
| 184 |
+
tokens_count,
|
| 185 |
+
message: Some("Token files have been updated and reloaded".to_string()),
|
| 186 |
+
}))
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
pub async fn handle_add_tokens(
|
| 190 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 191 |
+
headers: HeaderMap,
|
| 192 |
+
Json(request): Json<Vec<TokenAddRequestTokenInfo>>,
|
| 193 |
+
) -> Result<Json<TokenInfoResponse>, (StatusCode, Json<ErrorResponse>)> {
|
| 194 |
+
// 验证 AUTH_TOKEN
|
| 195 |
+
let auth_header = headers
|
| 196 |
+
.get(AUTHORIZATION)
|
| 197 |
+
.and_then(|h| h.to_str().ok())
|
| 198 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 199 |
+
.ok_or((
|
| 200 |
+
StatusCode::UNAUTHORIZED,
|
| 201 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 202 |
+
))?;
|
| 203 |
+
|
| 204 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 205 |
+
return Err((
|
| 206 |
+
StatusCode::UNAUTHORIZED,
|
| 207 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 208 |
+
));
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
let token_list_file = TOKEN_LIST_FILE.as_str();
|
| 212 |
+
|
| 213 |
+
// 获取当前的 tokens 并创建新的 token_infos
|
| 214 |
+
let mut token_infos = {
|
| 215 |
+
let state = state.lock().await;
|
| 216 |
+
state.token_infos.clone()
|
| 217 |
+
};
|
| 218 |
+
|
| 219 |
+
// 创建现有token的集合
|
| 220 |
+
let existing_tokens: std::collections::HashSet<_> =
|
| 221 |
+
token_infos.iter().map(|info| info.token.as_str()).collect();
|
| 222 |
+
|
| 223 |
+
// 预分配容量
|
| 224 |
+
let mut new_tokens = Vec::with_capacity(request.len());
|
| 225 |
+
|
| 226 |
+
// 处理新的tokens
|
| 227 |
+
for token_info in request {
|
| 228 |
+
let parsed_token = parse_token(&token_info.token);
|
| 229 |
+
if !existing_tokens.contains(parsed_token.as_str()) && validate_token(&parsed_token) {
|
| 230 |
+
new_tokens.push(TokenInfo {
|
| 231 |
+
token: parsed_token,
|
| 232 |
+
// 如果提供了checksum就使用提供的,否则生成新的
|
| 233 |
+
checksum: token_info
|
| 234 |
+
.checksum
|
| 235 |
+
.as_deref()
|
| 236 |
+
.map(generate_checksum_with_repair)
|
| 237 |
+
.unwrap_or_else(generate_checksum_with_default),
|
| 238 |
+
profile: None,
|
| 239 |
+
});
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
// 如果有新tokens才进行后续操作
|
| 244 |
+
if !new_tokens.is_empty() {
|
| 245 |
+
// 预分配足够的容量
|
| 246 |
+
token_infos.reserve(new_tokens.len());
|
| 247 |
+
token_infos.extend(new_tokens);
|
| 248 |
+
|
| 249 |
+
// 写入文件
|
| 250 |
+
write_tokens(&token_infos, token_list_file).map_err(|_| {
|
| 251 |
+
(
|
| 252 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 253 |
+
Json(ErrorResponse {
|
| 254 |
+
status: ApiStatus::Error,
|
| 255 |
+
code: None,
|
| 256 |
+
error: Some("Failed to update token list file".to_string()),
|
| 257 |
+
message: Some("无法更新token list文件".to_string()),
|
| 258 |
+
}),
|
| 259 |
+
)
|
| 260 |
+
})?;
|
| 261 |
+
|
| 262 |
+
// 获取最终的tokens数量(在更新状态之前)
|
| 263 |
+
let tokens_count = token_infos.len();
|
| 264 |
+
|
| 265 |
+
// 更新应用状态
|
| 266 |
+
{
|
| 267 |
+
let mut state = state.lock().await;
|
| 268 |
+
state.token_infos = token_infos;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
Ok(Json(TokenInfoResponse {
|
| 272 |
+
status: ApiStatus::Success,
|
| 273 |
+
tokens: None,
|
| 274 |
+
tokens_count,
|
| 275 |
+
message: Some("New tokens have been added and reloaded".to_string()),
|
| 276 |
+
}))
|
| 277 |
+
} else {
|
| 278 |
+
// 如果没有新tokens,使用原始数量
|
| 279 |
+
let tokens_count = token_infos.len();
|
| 280 |
+
|
| 281 |
+
Ok(Json(TokenInfoResponse {
|
| 282 |
+
status: ApiStatus::Success,
|
| 283 |
+
tokens: None,
|
| 284 |
+
tokens_count,
|
| 285 |
+
message: Some("No new tokens were added".to_string()),
|
| 286 |
+
}))
|
| 287 |
+
}
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
pub async fn handle_delete_tokens(
|
| 291 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 292 |
+
headers: HeaderMap,
|
| 293 |
+
Json(request): Json<TokensDeleteRequest>,
|
| 294 |
+
) -> Result<Json<TokensDeleteResponse>, (StatusCode, Json<ErrorResponse>)> {
|
| 295 |
+
// 验证 AUTH_TOKEN
|
| 296 |
+
let auth_header = headers
|
| 297 |
+
.get(AUTHORIZATION)
|
| 298 |
+
.and_then(|h| h.to_str().ok())
|
| 299 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 300 |
+
.ok_or((
|
| 301 |
+
StatusCode::UNAUTHORIZED,
|
| 302 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 303 |
+
))?;
|
| 304 |
+
|
| 305 |
+
if auth_header != AUTH_TOKEN.as_str() {
|
| 306 |
+
return Err((
|
| 307 |
+
StatusCode::UNAUTHORIZED,
|
| 308 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 309 |
+
));
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
let token_infos = state.lock().await.token_infos.clone();
|
| 313 |
+
let original_count = token_infos.len(); // 提前存储原始长度
|
| 314 |
+
|
| 315 |
+
// 获取token_list文件路径
|
| 316 |
+
let token_list_file = TOKEN_LIST_FILE.as_str();
|
| 317 |
+
|
| 318 |
+
// 创建要删除的tokens的HashSet,提高查找效率
|
| 319 |
+
let tokens_to_delete: std::collections::HashSet<_> = request.tokens.iter().collect();
|
| 320 |
+
|
| 321 |
+
// 如果需要的话计算 failed_tokens
|
| 322 |
+
let failed_tokens = if request.expectation.needs_failed_tokens() {
|
| 323 |
+
Some(
|
| 324 |
+
request
|
| 325 |
+
.tokens
|
| 326 |
+
.iter()
|
| 327 |
+
.filter(|token| !token_infos.iter().any(|info| &info.token == *token))
|
| 328 |
+
.cloned()
|
| 329 |
+
.collect::<Vec<String>>(),
|
| 330 |
+
)
|
| 331 |
+
} else {
|
| 332 |
+
None
|
| 333 |
+
};
|
| 334 |
+
|
| 335 |
+
// 预分配容量并过滤掉要删除的tokens
|
| 336 |
+
let estimated_capacity = original_count.saturating_sub(tokens_to_delete.len());
|
| 337 |
+
let mut filtered_token_infos = Vec::with_capacity(estimated_capacity);
|
| 338 |
+
|
| 339 |
+
// 一次性过滤tokens
|
| 340 |
+
for info in token_infos {
|
| 341 |
+
if !tokens_to_delete.contains(&info.token) {
|
| 342 |
+
filtered_token_infos.push(info);
|
| 343 |
+
}
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
// 如果有tokens被删除才进行更新操作
|
| 347 |
+
if filtered_token_infos.len() < original_count {
|
| 348 |
+
// 写入文件
|
| 349 |
+
write_tokens(&filtered_token_infos, token_list_file).map_err(|_| {
|
| 350 |
+
(
|
| 351 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 352 |
+
Json(ErrorResponse {
|
| 353 |
+
status: ApiStatus::Error,
|
| 354 |
+
code: None,
|
| 355 |
+
error: Some("Failed to update token list file".to_string()),
|
| 356 |
+
message: Some("无法更新token list文件".to_string()),
|
| 357 |
+
}),
|
| 358 |
+
)
|
| 359 |
+
})?;
|
| 360 |
+
|
| 361 |
+
// 如果需要的话计算 updated_tokens
|
| 362 |
+
let updated_tokens = if request.expectation.needs_updated_tokens() {
|
| 363 |
+
Some(
|
| 364 |
+
filtered_token_infos
|
| 365 |
+
.iter()
|
| 366 |
+
.map(|info| info.token.clone())
|
| 367 |
+
.collect(),
|
| 368 |
+
)
|
| 369 |
+
} else {
|
| 370 |
+
None
|
| 371 |
+
};
|
| 372 |
+
|
| 373 |
+
// 更新状态
|
| 374 |
+
{
|
| 375 |
+
let mut state = state.lock().await;
|
| 376 |
+
state.token_infos = filtered_token_infos;
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
Ok(Json(TokensDeleteResponse {
|
| 380 |
+
status: ApiStatus::Success,
|
| 381 |
+
updated_tokens,
|
| 382 |
+
failed_tokens,
|
| 383 |
+
}))
|
| 384 |
+
} else {
|
| 385 |
+
// 如果没有tokens被删除
|
| 386 |
+
Ok(Json(TokensDeleteResponse {
|
| 387 |
+
status: ApiStatus::Success,
|
| 388 |
+
updated_tokens: if request.expectation.needs_updated_tokens() {
|
| 389 |
+
Some(
|
| 390 |
+
filtered_token_infos
|
| 391 |
+
.iter()
|
| 392 |
+
.map(|info| info.token.clone())
|
| 393 |
+
.collect(),
|
| 394 |
+
)
|
| 395 |
+
} else {
|
| 396 |
+
None
|
| 397 |
+
},
|
| 398 |
+
failed_tokens,
|
| 399 |
+
}))
|
| 400 |
+
}
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
pub async fn handle_tokens_page() -> impl IntoResponse {
|
| 404 |
+
match AppConfig::get_page_content(ROUTE_TOKENS_PATH).unwrap_or_default() {
|
| 405 |
+
PageContent::Default => Response::builder()
|
| 406 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 407 |
+
.body(include_str!("../../../static/tokens.min.html").to_string())
|
| 408 |
+
.unwrap(),
|
| 409 |
+
PageContent::Text(content) => Response::builder()
|
| 410 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
|
| 411 |
+
.body(content.clone())
|
| 412 |
+
.unwrap(),
|
| 413 |
+
PageContent::Html(content) => Response::builder()
|
| 414 |
+
.header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
|
| 415 |
+
.body(content.clone())
|
| 416 |
+
.unwrap(),
|
| 417 |
+
}
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
#[derive(Deserialize)]
|
| 421 |
+
pub struct TokenRequest {
|
| 422 |
+
pub token: Option<String>,
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
#[derive(Serialize)]
|
| 426 |
+
pub struct BasicCalibrationResponse {
|
| 427 |
+
pub status: ApiStatus,
|
| 428 |
+
pub message: Option<String>,
|
| 429 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 430 |
+
pub user_id: Option<String>,
|
| 431 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 432 |
+
pub create_at: Option<String>,
|
| 433 |
+
#[serde(skip_serializing_if = "Option::is_none")]
|
| 434 |
+
pub checksum_time: Option<u64>,
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
pub async fn handle_basic_calibration(
|
| 438 |
+
Json(request): Json<TokenRequest>,
|
| 439 |
+
) -> Json<BasicCalibrationResponse> {
|
| 440 |
+
// 从请求头中获取并验证 auth token
|
| 441 |
+
let auth_token = match request.token {
|
| 442 |
+
Some(token) => token,
|
| 443 |
+
None => {
|
| 444 |
+
return Json(BasicCalibrationResponse {
|
| 445 |
+
status: ApiStatus::Error,
|
| 446 |
+
message: Some("未提供授权令牌".to_string()),
|
| 447 |
+
user_id: None,
|
| 448 |
+
create_at: None,
|
| 449 |
+
checksum_time: None,
|
| 450 |
+
})
|
| 451 |
+
}
|
| 452 |
+
};
|
| 453 |
+
|
| 454 |
+
// 校验 token 和 checksum
|
| 455 |
+
let (token, checksum) = match validate_token_and_checksum(&auth_token) {
|
| 456 |
+
Some(parts) => parts,
|
| 457 |
+
None => {
|
| 458 |
+
return Json(BasicCalibrationResponse {
|
| 459 |
+
status: ApiStatus::Error,
|
| 460 |
+
message: Some("无效令牌或无效校验和".to_string()),
|
| 461 |
+
user_id: None,
|
| 462 |
+
create_at: None,
|
| 463 |
+
checksum_time: None,
|
| 464 |
+
})
|
| 465 |
+
}
|
| 466 |
+
};
|
| 467 |
+
|
| 468 |
+
// 提取用户ID和创建时间
|
| 469 |
+
let user_id = extract_user_id(&token);
|
| 470 |
+
let create_at = extract_time(&token).map(|dt| dt.to_string());
|
| 471 |
+
let checksum_time = extract_time_ks(&checksum[..8]);
|
| 472 |
+
|
| 473 |
+
// 返回校验结果
|
| 474 |
+
Json(BasicCalibrationResponse {
|
| 475 |
+
status: ApiStatus::Success,
|
| 476 |
+
message: Some("校验成功".to_string()),
|
| 477 |
+
user_id,
|
| 478 |
+
create_at,
|
| 479 |
+
checksum_time,
|
| 480 |
+
})
|
| 481 |
+
}
|
src/chat/service.rs
ADDED
|
@@ -0,0 +1,766 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::{
|
| 2 |
+
app::{
|
| 3 |
+
constant::{
|
| 4 |
+
AUTHORIZATION_BEARER_PREFIX, FINISH_REASON_STOP, OBJECT_CHAT_COMPLETION,
|
| 5 |
+
OBJECT_CHAT_COMPLETION_CHUNK,
|
| 6 |
+
},
|
| 7 |
+
lazy::{AUTH_TOKEN, KEY_PREFIX, KEY_PREFIX_LEN, REQUEST_LOGS_LIMIT, SERVICE_TIMEOUT},
|
| 8 |
+
model::{
|
| 9 |
+
AppConfig, AppState, ChatRequest, LogStatus, RequestLog, TimingInfo, TokenInfo,
|
| 10 |
+
UsageCheck,
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
chat::{
|
| 14 |
+
config::KeyConfig,
|
| 15 |
+
constant::{AVAILABLE_MODELS, USAGE_CHECK_MODELS},
|
| 16 |
+
error::StreamError,
|
| 17 |
+
model::{
|
| 18 |
+
ChatResponse, Choice, Delta, Message, MessageContent, ModelsResponse, Role, Usage,
|
| 19 |
+
},
|
| 20 |
+
stream::{StreamDecoder, StreamMessage},
|
| 21 |
+
},
|
| 22 |
+
common::{
|
| 23 |
+
client::build_client,
|
| 24 |
+
model::{error::ChatError, userinfo::MembershipType, ApiStatus, ErrorResponse},
|
| 25 |
+
utils::{
|
| 26 |
+
format_time_ms, from_base64, get_token_profile, tokeninfo_to_token,
|
| 27 |
+
validate_token_and_checksum, TrimNewlines as _,
|
| 28 |
+
},
|
| 29 |
+
},
|
| 30 |
+
};
|
| 31 |
+
use axum::{
|
| 32 |
+
body::Body,
|
| 33 |
+
extract::State,
|
| 34 |
+
http::{
|
| 35 |
+
header::{AUTHORIZATION, CONTENT_TYPE},
|
| 36 |
+
HeaderMap, StatusCode,
|
| 37 |
+
},
|
| 38 |
+
response::Response,
|
| 39 |
+
Json,
|
| 40 |
+
};
|
| 41 |
+
use bytes::Bytes;
|
| 42 |
+
use futures::StreamExt;
|
| 43 |
+
use prost::Message as _;
|
| 44 |
+
use std::sync::atomic::{AtomicUsize, Ordering};
|
| 45 |
+
use std::{
|
| 46 |
+
convert::Infallible,
|
| 47 |
+
sync::{atomic::AtomicBool, Arc},
|
| 48 |
+
};
|
| 49 |
+
use tokio::sync::Mutex;
|
| 50 |
+
use uuid::Uuid;
|
| 51 |
+
|
| 52 |
+
// 模型列表处理
|
| 53 |
+
pub async fn handle_models() -> Json<ModelsResponse> {
|
| 54 |
+
Json(ModelsResponse {
|
| 55 |
+
object: "list",
|
| 56 |
+
data: &AVAILABLE_MODELS,
|
| 57 |
+
})
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
// 聊天处理函数的签名
|
| 61 |
+
pub async fn handle_chat(
|
| 62 |
+
State(state): State<Arc<Mutex<AppState>>>,
|
| 63 |
+
headers: HeaderMap,
|
| 64 |
+
Json(request): Json<ChatRequest>,
|
| 65 |
+
) -> Result<Response<Body>, (StatusCode, Json<ErrorResponse>)> {
|
| 66 |
+
let allow_claude = AppConfig::get_allow_claude();
|
| 67 |
+
|
| 68 |
+
let is_search = request.model.ends_with("-online");
|
| 69 |
+
let model_name = if is_search {
|
| 70 |
+
request.model[..request.model.len() - 7].to_string()
|
| 71 |
+
} else {
|
| 72 |
+
request.model.clone()
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
// 验证模型是否支持并获取模型信息
|
| 76 |
+
let model = AVAILABLE_MODELS.iter().find(|m| m.id == model_name);
|
| 77 |
+
let model_supported = model.is_some();
|
| 78 |
+
|
| 79 |
+
if !(model_supported || allow_claude && request.model.starts_with("claude")) {
|
| 80 |
+
return Err((
|
| 81 |
+
StatusCode::BAD_REQUEST,
|
| 82 |
+
Json(ChatError::ModelNotSupported(request.model).to_json()),
|
| 83 |
+
));
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
let request_time = chrono::Local::now();
|
| 87 |
+
|
| 88 |
+
// 验证请求
|
| 89 |
+
if request.messages.is_empty() {
|
| 90 |
+
return Err((
|
| 91 |
+
StatusCode::BAD_REQUEST,
|
| 92 |
+
Json(ChatError::EmptyMessages.to_json()),
|
| 93 |
+
));
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// 获取并处理认证令牌
|
| 97 |
+
let auth_header = headers
|
| 98 |
+
.get(AUTHORIZATION)
|
| 99 |
+
.and_then(|h| h.to_str().ok())
|
| 100 |
+
.and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
|
| 101 |
+
.ok_or((
|
| 102 |
+
StatusCode::UNAUTHORIZED,
|
| 103 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 104 |
+
))?;
|
| 105 |
+
|
| 106 |
+
let mut current_config = KeyConfig::new_with_global();
|
| 107 |
+
|
| 108 |
+
// 验证认证token并获取token信息
|
| 109 |
+
let (auth_token, checksum) = match auth_header {
|
| 110 |
+
// 管理员Token验证逻辑
|
| 111 |
+
token
|
| 112 |
+
if token == AUTH_TOKEN.as_str()
|
| 113 |
+
|| (AppConfig::is_share() && token == AppConfig::get_share_token().as_str()) =>
|
| 114 |
+
{
|
| 115 |
+
static CURRENT_KEY_INDEX: AtomicUsize = AtomicUsize::new(0);
|
| 116 |
+
let state_guard = state.lock().await;
|
| 117 |
+
let token_infos = &state_guard.token_infos;
|
| 118 |
+
|
| 119 |
+
// 检查是否存在可用的token
|
| 120 |
+
if token_infos.is_empty() {
|
| 121 |
+
return Err((
|
| 122 |
+
StatusCode::SERVICE_UNAVAILABLE,
|
| 123 |
+
Json(ChatError::NoTokens.to_json()),
|
| 124 |
+
));
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
// 轮询选择token
|
| 128 |
+
let index = CURRENT_KEY_INDEX.fetch_add(1, Ordering::SeqCst) % token_infos.len();
|
| 129 |
+
let token_info = &token_infos[index];
|
| 130 |
+
(token_info.token.clone(), token_info.checksum.clone())
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
token if AppConfig::get_dynamic_key() && token.starts_with(&*KEY_PREFIX) => {
|
| 134 |
+
from_base64(&token[*KEY_PREFIX_LEN..])
|
| 135 |
+
.and_then(|decoded_bytes| KeyConfig::decode(&decoded_bytes[..]).ok())
|
| 136 |
+
.and_then(|key_config| {
|
| 137 |
+
key_config.copy_without_auth_token(&mut current_config);
|
| 138 |
+
key_config.auth_token
|
| 139 |
+
})
|
| 140 |
+
.and_then(|token_info| tokeninfo_to_token(&token_info))
|
| 141 |
+
.ok_or((
|
| 142 |
+
StatusCode::UNAUTHORIZED,
|
| 143 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 144 |
+
))?
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
// 普通用户Token验证逻辑
|
| 148 |
+
token => validate_token_and_checksum(token).ok_or((
|
| 149 |
+
StatusCode::UNAUTHORIZED,
|
| 150 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 151 |
+
))?,
|
| 152 |
+
};
|
| 153 |
+
|
| 154 |
+
let current_config = current_config;
|
| 155 |
+
|
| 156 |
+
let current_id: u64;
|
| 157 |
+
|
| 158 |
+
// 更新请求日志
|
| 159 |
+
{
|
| 160 |
+
let state_clone = state.clone();
|
| 161 |
+
let mut state = state.lock().await;
|
| 162 |
+
state.total_requests += 1;
|
| 163 |
+
state.active_requests += 1;
|
| 164 |
+
|
| 165 |
+
// 查找最新的相同token的日志,检查使用情况
|
| 166 |
+
let need_profile_check = state
|
| 167 |
+
.request_logs
|
| 168 |
+
.iter()
|
| 169 |
+
.rev()
|
| 170 |
+
.find(|log| log.token_info.token == auth_token && log.token_info.profile.is_some())
|
| 171 |
+
.and_then(|log| log.token_info.profile.as_ref())
|
| 172 |
+
.map(|profile| {
|
| 173 |
+
if profile.stripe.membership_type != MembershipType::Free {
|
| 174 |
+
return false;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
let is_premium = USAGE_CHECK_MODELS.contains(&model_name.as_str());
|
| 178 |
+
let standard = &profile.usage.standard;
|
| 179 |
+
let premium = &profile.usage.premium;
|
| 180 |
+
|
| 181 |
+
if is_premium {
|
| 182 |
+
premium
|
| 183 |
+
.max_requests
|
| 184 |
+
.map_or(false, |max| premium.num_requests >= max)
|
| 185 |
+
} else {
|
| 186 |
+
standard
|
| 187 |
+
.max_requests
|
| 188 |
+
.map_or(false, |max| standard.num_requests >= max)
|
| 189 |
+
}
|
| 190 |
+
})
|
| 191 |
+
.unwrap_or(false);
|
| 192 |
+
|
| 193 |
+
// 如果达到限制,直接返回未授权错误
|
| 194 |
+
if need_profile_check {
|
| 195 |
+
state.active_requests -= 1;
|
| 196 |
+
state.error_requests += 1;
|
| 197 |
+
return Err((
|
| 198 |
+
StatusCode::UNAUTHORIZED,
|
| 199 |
+
Json(ChatError::Unauthorized.to_json()),
|
| 200 |
+
));
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
let next_id = state.request_logs.last().map_or(1, |log| log.id + 1);
|
| 204 |
+
current_id = next_id;
|
| 205 |
+
|
| 206 |
+
// 如果需要获取用户使用情况,创建后台任务获取profile
|
| 207 |
+
if model
|
| 208 |
+
.map(|m| {
|
| 209 |
+
m.is_usage_check(UsageCheck::from_proto(
|
| 210 |
+
current_config.usage_check_models.as_ref(),
|
| 211 |
+
))
|
| 212 |
+
})
|
| 213 |
+
.unwrap_or(false)
|
| 214 |
+
{
|
| 215 |
+
let auth_token_clone = auth_token.clone();
|
| 216 |
+
let state_clone = state_clone.clone();
|
| 217 |
+
let log_id = next_id;
|
| 218 |
+
|
| 219 |
+
tokio::spawn(async move {
|
| 220 |
+
let profile = get_token_profile(&auth_token_clone).await;
|
| 221 |
+
let mut state = state_clone.lock().await;
|
| 222 |
+
|
| 223 |
+
// 先找到所有需要更新的位置的索引
|
| 224 |
+
let token_info_idx = state
|
| 225 |
+
.token_infos
|
| 226 |
+
.iter()
|
| 227 |
+
.position(|info| info.token == auth_token_clone);
|
| 228 |
+
|
| 229 |
+
let log_idx = state.request_logs.iter().rposition(|log| log.id == log_id);
|
| 230 |
+
|
| 231 |
+
// 根据索引更新
|
| 232 |
+
match (token_info_idx, log_idx) {
|
| 233 |
+
(Some(t_idx), Some(l_idx)) => {
|
| 234 |
+
state.token_infos[t_idx].profile = profile.clone();
|
| 235 |
+
state.request_logs[l_idx].token_info.profile = profile;
|
| 236 |
+
}
|
| 237 |
+
(Some(t_idx), None) => {
|
| 238 |
+
state.token_infos[t_idx].profile = profile;
|
| 239 |
+
}
|
| 240 |
+
(None, Some(l_idx)) => {
|
| 241 |
+
state.request_logs[l_idx].token_info.profile = profile;
|
| 242 |
+
}
|
| 243 |
+
(None, None) => {}
|
| 244 |
+
}
|
| 245 |
+
});
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
state.request_logs.push(RequestLog {
|
| 249 |
+
id: next_id,
|
| 250 |
+
timestamp: request_time,
|
| 251 |
+
model: request.model.clone(),
|
| 252 |
+
token_info: TokenInfo {
|
| 253 |
+
token: auth_token.clone(),
|
| 254 |
+
checksum: checksum.clone(),
|
| 255 |
+
profile: None,
|
| 256 |
+
},
|
| 257 |
+
prompt: None,
|
| 258 |
+
timing: TimingInfo {
|
| 259 |
+
total: 0.0,
|
| 260 |
+
first: None,
|
| 261 |
+
},
|
| 262 |
+
stream: request.stream,
|
| 263 |
+
status: LogStatus::Pending,
|
| 264 |
+
error: None,
|
| 265 |
+
});
|
| 266 |
+
|
| 267 |
+
if state.request_logs.len() > *REQUEST_LOGS_LIMIT {
|
| 268 |
+
state.request_logs.remove(0);
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
// 将消息转换为hex格式
|
| 273 |
+
let hex_data = match super::adapter::encode_chat_message(
|
| 274 |
+
request.messages,
|
| 275 |
+
&model_name,
|
| 276 |
+
current_config.disable_vision(),
|
| 277 |
+
current_config.enable_slow_pool(),
|
| 278 |
+
is_search,
|
| 279 |
+
)
|
| 280 |
+
.await
|
| 281 |
+
{
|
| 282 |
+
Ok(data) => data,
|
| 283 |
+
Err(e) => {
|
| 284 |
+
let mut state = state.lock().await;
|
| 285 |
+
if let Some(log) = state
|
| 286 |
+
.request_logs
|
| 287 |
+
.iter_mut()
|
| 288 |
+
.rev()
|
| 289 |
+
.find(|log| log.id == current_id)
|
| 290 |
+
{
|
| 291 |
+
log.status = LogStatus::Failed;
|
| 292 |
+
log.error = Some(e.to_string());
|
| 293 |
+
}
|
| 294 |
+
state.active_requests -= 1;
|
| 295 |
+
state.error_requests += 1;
|
| 296 |
+
return Err((
|
| 297 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 298 |
+
Json(
|
| 299 |
+
ChatError::RequestFailed("Failed to encode chat message".to_string()).to_json(),
|
| 300 |
+
),
|
| 301 |
+
));
|
| 302 |
+
}
|
| 303 |
+
};
|
| 304 |
+
|
| 305 |
+
// 构建请求客户端
|
| 306 |
+
let client = build_client(&auth_token, &checksum, is_search);
|
| 307 |
+
// 添加超时设置
|
| 308 |
+
let response = tokio::time::timeout(
|
| 309 |
+
std::time::Duration::from_secs(*SERVICE_TIMEOUT),
|
| 310 |
+
client.body(hex_data).send(),
|
| 311 |
+
)
|
| 312 |
+
.await;
|
| 313 |
+
|
| 314 |
+
// 处理请求结果
|
| 315 |
+
let response = match response {
|
| 316 |
+
Ok(inner_response) => match inner_response {
|
| 317 |
+
Ok(resp) => {
|
| 318 |
+
// 更新请求日志为成功
|
| 319 |
+
{
|
| 320 |
+
let mut state = state.lock().await;
|
| 321 |
+
if let Some(log) = state
|
| 322 |
+
.request_logs
|
| 323 |
+
.iter_mut()
|
| 324 |
+
.rev()
|
| 325 |
+
.find(|log| log.id == current_id)
|
| 326 |
+
{
|
| 327 |
+
log.status = LogStatus::Success;
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
resp
|
| 331 |
+
}
|
| 332 |
+
Err(e) => {
|
| 333 |
+
// 更新请求日志为失败
|
| 334 |
+
{
|
| 335 |
+
let mut state = state.lock().await;
|
| 336 |
+
if let Some(log) = state
|
| 337 |
+
.request_logs
|
| 338 |
+
.iter_mut()
|
| 339 |
+
.rev()
|
| 340 |
+
.find(|log| log.id == current_id)
|
| 341 |
+
{
|
| 342 |
+
log.status = LogStatus::Failed;
|
| 343 |
+
log.error = Some(e.to_string());
|
| 344 |
+
}
|
| 345 |
+
state.active_requests -= 1;
|
| 346 |
+
state.error_requests += 1;
|
| 347 |
+
}
|
| 348 |
+
return Err((
|
| 349 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 350 |
+
Json(ChatError::RequestFailed(e.to_string()).to_json()),
|
| 351 |
+
));
|
| 352 |
+
}
|
| 353 |
+
},
|
| 354 |
+
Err(_) => {
|
| 355 |
+
// 处理超时错误
|
| 356 |
+
{
|
| 357 |
+
let mut state = state.lock().await;
|
| 358 |
+
if let Some(log) = state
|
| 359 |
+
.request_logs
|
| 360 |
+
.iter_mut()
|
| 361 |
+
.rev()
|
| 362 |
+
.find(|log| log.id == current_id)
|
| 363 |
+
{
|
| 364 |
+
log.status = LogStatus::Failed;
|
| 365 |
+
log.error = Some("Request timeout".to_string());
|
| 366 |
+
}
|
| 367 |
+
state.active_requests -= 1;
|
| 368 |
+
state.error_requests += 1;
|
| 369 |
+
}
|
| 370 |
+
return Err((
|
| 371 |
+
StatusCode::GATEWAY_TIMEOUT,
|
| 372 |
+
Json(ChatError::RequestFailed("Request timeout".to_string()).to_json()),
|
| 373 |
+
));
|
| 374 |
+
}
|
| 375 |
+
};
|
| 376 |
+
|
| 377 |
+
// 释放活动请求计数
|
| 378 |
+
{
|
| 379 |
+
let mut state = state.lock().await;
|
| 380 |
+
state.active_requests -= 1;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
let convert_web_ref = current_config.include_web_references();
|
| 384 |
+
|
| 385 |
+
if request.stream {
|
| 386 |
+
let response_id = format!("chatcmpl-{}", Uuid::new_v4().simple());
|
| 387 |
+
let is_start = Arc::new(AtomicBool::new(true));
|
| 388 |
+
let start_time = std::time::Instant::now();
|
| 389 |
+
let first_chunk_time = Arc::new(Mutex::new(None::<f64>));
|
| 390 |
+
let decoder = Arc::new(Mutex::new(StreamDecoder::new()));
|
| 391 |
+
|
| 392 |
+
// 定义消息处理器的上下文结构体
|
| 393 |
+
struct MessageProcessContext<'a> {
|
| 394 |
+
response_id: &'a str,
|
| 395 |
+
model: &'a str,
|
| 396 |
+
is_start: &'a AtomicBool,
|
| 397 |
+
first_chunk_time: &'a Mutex<Option<f64>>,
|
| 398 |
+
start_time: std::time::Instant,
|
| 399 |
+
state: &'a Mutex<AppState>,
|
| 400 |
+
current_id: u64,
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
// 处理消息并生成响应数据的辅助函数
|
| 404 |
+
async fn process_messages(
|
| 405 |
+
messages: Vec<StreamMessage>,
|
| 406 |
+
ctx: &MessageProcessContext<'_>,
|
| 407 |
+
) -> String {
|
| 408 |
+
let mut response_data = String::new();
|
| 409 |
+
|
| 410 |
+
for message in messages {
|
| 411 |
+
match message {
|
| 412 |
+
StreamMessage::Content(text) => {
|
| 413 |
+
let is_first = ctx.is_start.load(Ordering::SeqCst);
|
| 414 |
+
if is_first {
|
| 415 |
+
if let Ok(mut first_time) = ctx.first_chunk_time.try_lock() {
|
| 416 |
+
*first_time = Some(ctx.start_time.elapsed().as_secs_f64());
|
| 417 |
+
}
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
let response = ChatResponse {
|
| 421 |
+
id: ctx.response_id.to_string(),
|
| 422 |
+
object: OBJECT_CHAT_COMPLETION_CHUNK.to_string(),
|
| 423 |
+
created: chrono::Utc::now().timestamp(),
|
| 424 |
+
model: if is_first {
|
| 425 |
+
Some(ctx.model.to_string())
|
| 426 |
+
} else {
|
| 427 |
+
None
|
| 428 |
+
},
|
| 429 |
+
choices: vec![Choice {
|
| 430 |
+
index: 0,
|
| 431 |
+
message: None,
|
| 432 |
+
delta: Some(Delta {
|
| 433 |
+
role: if is_first {
|
| 434 |
+
Some(Role::Assistant)
|
| 435 |
+
} else {
|
| 436 |
+
None
|
| 437 |
+
},
|
| 438 |
+
content: if is_first {
|
| 439 |
+
ctx.is_start.store(false, Ordering::SeqCst);
|
| 440 |
+
Some(text.trim_leading_newlines())
|
| 441 |
+
} else {
|
| 442 |
+
Some(text)
|
| 443 |
+
},
|
| 444 |
+
}),
|
| 445 |
+
finish_reason: None,
|
| 446 |
+
}],
|
| 447 |
+
usage: None,
|
| 448 |
+
};
|
| 449 |
+
|
| 450 |
+
response_data.push_str(&format!(
|
| 451 |
+
"data: {}\n\n",
|
| 452 |
+
serde_json::to_string(&response).unwrap()
|
| 453 |
+
));
|
| 454 |
+
}
|
| 455 |
+
StreamMessage::StreamEnd => {
|
| 456 |
+
// 计算总时间和首次片段时间
|
| 457 |
+
let total_time = ctx.start_time.elapsed().as_secs_f64();
|
| 458 |
+
let first_time = ctx.first_chunk_time.lock().await.unwrap_or(total_time);
|
| 459 |
+
|
| 460 |
+
{
|
| 461 |
+
let mut state = ctx.state.lock().await;
|
| 462 |
+
if let Some(log) = state
|
| 463 |
+
.request_logs
|
| 464 |
+
.iter_mut()
|
| 465 |
+
.rev()
|
| 466 |
+
.find(|log| log.id == ctx.current_id)
|
| 467 |
+
{
|
| 468 |
+
log.timing.total = format_time_ms(total_time);
|
| 469 |
+
log.timing.first = Some(format_time_ms(first_time));
|
| 470 |
+
}
|
| 471 |
+
}
|
| 472 |
+
|
| 473 |
+
let response = ChatResponse {
|
| 474 |
+
id: ctx.response_id.to_string(),
|
| 475 |
+
object: OBJECT_CHAT_COMPLETION_CHUNK.to_string(),
|
| 476 |
+
created: chrono::Utc::now().timestamp(),
|
| 477 |
+
model: None,
|
| 478 |
+
choices: vec![Choice {
|
| 479 |
+
index: 0,
|
| 480 |
+
message: None,
|
| 481 |
+
delta: Some(Delta {
|
| 482 |
+
role: None,
|
| 483 |
+
content: None,
|
| 484 |
+
}),
|
| 485 |
+
finish_reason: Some(FINISH_REASON_STOP.to_string()),
|
| 486 |
+
}],
|
| 487 |
+
usage: None,
|
| 488 |
+
};
|
| 489 |
+
response_data.push_str(&format!(
|
| 490 |
+
"data: {}\n\ndata: [DONE]\n\n",
|
| 491 |
+
serde_json::to_string(&response).unwrap()
|
| 492 |
+
));
|
| 493 |
+
}
|
| 494 |
+
StreamMessage::Debug(debug_prompt) => {
|
| 495 |
+
if let Ok(mut state) = ctx.state.try_lock() {
|
| 496 |
+
if let Some(log) = state
|
| 497 |
+
.request_logs
|
| 498 |
+
.iter_mut()
|
| 499 |
+
.rev()
|
| 500 |
+
.find(|log| log.id == ctx.current_id)
|
| 501 |
+
{
|
| 502 |
+
log.prompt = Some(debug_prompt);
|
| 503 |
+
}
|
| 504 |
+
}
|
| 505 |
+
}
|
| 506 |
+
_ => {} // 忽略其他消息类型
|
| 507 |
+
}
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
response_data
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
// 首先处理stream直到获得第一个结果
|
| 514 |
+
let mut stream = response.bytes_stream();
|
| 515 |
+
while !decoder.lock().await.is_first_result_ready() {
|
| 516 |
+
match stream.next().await {
|
| 517 |
+
Some(Ok(chunk)) => {
|
| 518 |
+
if let Err(StreamError::ChatError(error)) =
|
| 519 |
+
decoder.lock().await.decode(&chunk, convert_web_ref)
|
| 520 |
+
{
|
| 521 |
+
let error_response = error.to_error_response();
|
| 522 |
+
// 更新请求日志为失败
|
| 523 |
+
{
|
| 524 |
+
let mut state = state.lock().await;
|
| 525 |
+
if let Some(log) = state
|
| 526 |
+
.request_logs
|
| 527 |
+
.iter_mut()
|
| 528 |
+
.rev()
|
| 529 |
+
.find(|log| log.id == current_id)
|
| 530 |
+
{
|
| 531 |
+
log.status = LogStatus::Failed;
|
| 532 |
+
log.error = Some(error_response.native_code());
|
| 533 |
+
log.timing.total =
|
| 534 |
+
format_time_ms(start_time.elapsed().as_secs_f64());
|
| 535 |
+
state.error_requests += 1;
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
+
return Err((
|
| 539 |
+
error_response.status_code(),
|
| 540 |
+
Json(error_response.to_common()),
|
| 541 |
+
));
|
| 542 |
+
}
|
| 543 |
+
}
|
| 544 |
+
Some(Err(e)) => {
|
| 545 |
+
let error_message = format!("Failed to read response chunk: {}", e);
|
| 546 |
+
return Err((
|
| 547 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 548 |
+
Json(ChatError::RequestFailed(error_message).to_json()),
|
| 549 |
+
));
|
| 550 |
+
}
|
| 551 |
+
None => {
|
| 552 |
+
// 更新请求日志为失败
|
| 553 |
+
{
|
| 554 |
+
let mut state = state.lock().await;
|
| 555 |
+
if let Some(log) = state
|
| 556 |
+
.request_logs
|
| 557 |
+
.iter_mut()
|
| 558 |
+
.rev()
|
| 559 |
+
.find(|log| log.id == current_id)
|
| 560 |
+
{
|
| 561 |
+
log.status = LogStatus::Failed;
|
| 562 |
+
log.error = Some("Empty stream response".to_string());
|
| 563 |
+
state.error_requests += 1;
|
| 564 |
+
}
|
| 565 |
+
}
|
| 566 |
+
return Err((
|
| 567 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 568 |
+
Json(
|
| 569 |
+
ChatError::RequestFailed("Empty stream response".to_string()).to_json(),
|
| 570 |
+
),
|
| 571 |
+
));
|
| 572 |
+
}
|
| 573 |
+
}
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
// 处理后续的stream
|
| 577 |
+
let stream = stream.then({
|
| 578 |
+
let decoder = decoder.clone();
|
| 579 |
+
let response_id = response_id.clone();
|
| 580 |
+
let model = request.model.clone();
|
| 581 |
+
let is_start = is_start.clone();
|
| 582 |
+
let first_chunk_time = first_chunk_time.clone();
|
| 583 |
+
let state = state.clone();
|
| 584 |
+
|
| 585 |
+
move |chunk| {
|
| 586 |
+
let decoder = decoder.clone();
|
| 587 |
+
let response_id = response_id.clone();
|
| 588 |
+
let model = model.clone();
|
| 589 |
+
let is_start = is_start.clone();
|
| 590 |
+
let first_chunk_time = first_chunk_time.clone();
|
| 591 |
+
let state = state.clone();
|
| 592 |
+
|
| 593 |
+
async move {
|
| 594 |
+
let chunk = chunk.unwrap_or_default();
|
| 595 |
+
|
| 596 |
+
let ctx = MessageProcessContext {
|
| 597 |
+
response_id: &response_id,
|
| 598 |
+
model: &model,
|
| 599 |
+
is_start: &is_start,
|
| 600 |
+
first_chunk_time: &first_chunk_time,
|
| 601 |
+
start_time,
|
| 602 |
+
state: &state,
|
| 603 |
+
current_id,
|
| 604 |
+
};
|
| 605 |
+
|
| 606 |
+
// 使用decoder处理chunk
|
| 607 |
+
let messages = match decoder.lock().await.decode(&chunk, convert_web_ref) {
|
| 608 |
+
Ok(msgs) => msgs,
|
| 609 |
+
Err(e) => {
|
| 610 |
+
eprintln!("[警告] Stream error: {}", e);
|
| 611 |
+
return Ok::<_, Infallible>(Bytes::new());
|
| 612 |
+
}
|
| 613 |
+
};
|
| 614 |
+
|
| 615 |
+
let mut response_data = String::new();
|
| 616 |
+
|
| 617 |
+
if let Some(first_msg) = decoder.lock().await.take_first_result() {
|
| 618 |
+
let first_response = process_messages(first_msg, &ctx).await;
|
| 619 |
+
response_data.push_str(&first_response);
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
let current_response = process_messages(messages, &ctx).await;
|
| 623 |
+
if !current_response.is_empty() {
|
| 624 |
+
response_data.push_str(¤t_response);
|
| 625 |
+
}
|
| 626 |
+
|
| 627 |
+
Ok(Bytes::from(response_data))
|
| 628 |
+
}
|
| 629 |
+
}
|
| 630 |
+
});
|
| 631 |
+
|
| 632 |
+
Ok(Response::builder()
|
| 633 |
+
.header("Cache-Control", "no-cache")
|
| 634 |
+
.header("Connection", "keep-alive")
|
| 635 |
+
.header(CONTENT_TYPE, "text/event-stream")
|
| 636 |
+
.body(Body::from_stream(stream))
|
| 637 |
+
.unwrap())
|
| 638 |
+
} else {
|
| 639 |
+
// 非流式响应
|
| 640 |
+
let start_time = std::time::Instant::now();
|
| 641 |
+
let mut first_chunk_time = None::<f64>;
|
| 642 |
+
let mut decoder = StreamDecoder::new();
|
| 643 |
+
let mut full_text = String::with_capacity(1024);
|
| 644 |
+
let mut stream = response.bytes_stream();
|
| 645 |
+
|
| 646 |
+
// 逐个处理chunks
|
| 647 |
+
while let Some(chunk) = stream.next().await {
|
| 648 |
+
let chunk = chunk.map_err(|e| {
|
| 649 |
+
let error_message = format!("Failed to read response chunk: {}", e);
|
| 650 |
+
(
|
| 651 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 652 |
+
Json(ChatError::RequestFailed(error_message).to_json()),
|
| 653 |
+
)
|
| 654 |
+
})?;
|
| 655 |
+
|
| 656 |
+
// 立即处理当前chunk
|
| 657 |
+
match decoder.decode(&chunk, convert_web_ref) {
|
| 658 |
+
Ok(messages) => {
|
| 659 |
+
for message in messages {
|
| 660 |
+
match message {
|
| 661 |
+
StreamMessage::Content(text) => {
|
| 662 |
+
if first_chunk_time.is_none() {
|
| 663 |
+
first_chunk_time = Some(start_time.elapsed().as_secs_f64());
|
| 664 |
+
}
|
| 665 |
+
full_text.push_str(&text);
|
| 666 |
+
}
|
| 667 |
+
StreamMessage::Debug(debug_prompt) => {
|
| 668 |
+
if let Ok(mut state) = state.try_lock() {
|
| 669 |
+
if let Some(log) = state
|
| 670 |
+
.request_logs
|
| 671 |
+
.iter_mut()
|
| 672 |
+
.rev()
|
| 673 |
+
.find(|log| log.id == current_id)
|
| 674 |
+
{
|
| 675 |
+
log.prompt = Some(debug_prompt);
|
| 676 |
+
}
|
| 677 |
+
}
|
| 678 |
+
}
|
| 679 |
+
_ => {}
|
| 680 |
+
}
|
| 681 |
+
}
|
| 682 |
+
}
|
| 683 |
+
Err(StreamError::ChatError(error)) => {
|
| 684 |
+
let error_response = error.to_error_response();
|
| 685 |
+
return Err((
|
| 686 |
+
error_response.status_code(),
|
| 687 |
+
Json(error_response.to_common()),
|
| 688 |
+
));
|
| 689 |
+
}
|
| 690 |
+
Err(e) => {
|
| 691 |
+
let error_response = ErrorResponse {
|
| 692 |
+
status: ApiStatus::Error,
|
| 693 |
+
code: Some(500),
|
| 694 |
+
error: Some(e.to_string()),
|
| 695 |
+
message: None,
|
| 696 |
+
};
|
| 697 |
+
return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(error_response)));
|
| 698 |
+
}
|
| 699 |
+
}
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
// 检查响应是否为空
|
| 703 |
+
if full_text.is_empty() {
|
| 704 |
+
// 更新请求日志为失败
|
| 705 |
+
{
|
| 706 |
+
let mut state = state.lock().await;
|
| 707 |
+
if let Some(log) = state
|
| 708 |
+
.request_logs
|
| 709 |
+
.iter_mut()
|
| 710 |
+
.rev()
|
| 711 |
+
.find(|log| log.id == current_id)
|
| 712 |
+
{
|
| 713 |
+
log.status = LogStatus::Failed;
|
| 714 |
+
log.error = Some("Empty response received".to_string());
|
| 715 |
+
state.error_requests += 1;
|
| 716 |
+
}
|
| 717 |
+
}
|
| 718 |
+
return Err((
|
| 719 |
+
StatusCode::INTERNAL_SERVER_ERROR,
|
| 720 |
+
Json(ChatError::RequestFailed("Empty response received".to_string()).to_json()),
|
| 721 |
+
));
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
let response_data = ChatResponse {
|
| 725 |
+
id: format!("chatcmpl-{}", Uuid::new_v4().simple()),
|
| 726 |
+
object: OBJECT_CHAT_COMPLETION.to_string(),
|
| 727 |
+
created: chrono::Utc::now().timestamp(),
|
| 728 |
+
model: Some(request.model),
|
| 729 |
+
choices: vec![Choice {
|
| 730 |
+
index: 0,
|
| 731 |
+
message: Some(Message {
|
| 732 |
+
role: Role::Assistant,
|
| 733 |
+
content: MessageContent::Text(full_text.trim_leading_newlines()),
|
| 734 |
+
}),
|
| 735 |
+
delta: None,
|
| 736 |
+
finish_reason: Some(FINISH_REASON_STOP.to_string()),
|
| 737 |
+
}],
|
| 738 |
+
usage: Some(Usage {
|
| 739 |
+
prompt_tokens: 0,
|
| 740 |
+
completion_tokens: 0,
|
| 741 |
+
total_tokens: 0,
|
| 742 |
+
}),
|
| 743 |
+
};
|
| 744 |
+
|
| 745 |
+
{
|
| 746 |
+
// 更新请求日志时间信息和状态
|
| 747 |
+
let total_time = format_time_ms(start_time.elapsed().as_secs_f64());
|
| 748 |
+
let mut state = state.lock().await;
|
| 749 |
+
if let Some(log) = state
|
| 750 |
+
.request_logs
|
| 751 |
+
.iter_mut()
|
| 752 |
+
.rev()
|
| 753 |
+
.find(|log| log.id == current_id)
|
| 754 |
+
{
|
| 755 |
+
log.timing.total = total_time;
|
| 756 |
+
log.timing.first = first_chunk_time;
|
| 757 |
+
log.status = LogStatus::Success;
|
| 758 |
+
}
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
Ok(Response::builder()
|
| 762 |
+
.header(CONTENT_TYPE, "application/json")
|
| 763 |
+
.body(Body::from(serde_json::to_string(&response_data).unwrap()))
|
| 764 |
+
.unwrap())
|
| 765 |
+
}
|
| 766 |
+
}
|
src/chat/stream.rs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mod decoder;
|
| 2 |
+
pub use decoder::*;
|
src/chat/stream/decoder.rs
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use crate::chat::{
|
| 2 |
+
aiserver::v1::StreamChatResponse,
|
| 3 |
+
error::{ChatError, StreamError},
|
| 4 |
+
};
|
| 5 |
+
use flate2::read::GzDecoder;
|
| 6 |
+
use prost::Message;
|
| 7 |
+
use std::{collections::BTreeMap, io::Read};
|
| 8 |
+
|
| 9 |
+
// 解压gzip数据
|
| 10 |
+
fn decompress_gzip(data: &[u8]) -> Option<Vec<u8>> {
|
| 11 |
+
let mut decoder = GzDecoder::new(data);
|
| 12 |
+
let mut decompressed = Vec::new();
|
| 13 |
+
|
| 14 |
+
match decoder.read_to_end(&mut decompressed) {
|
| 15 |
+
Ok(_) => Some(decompressed),
|
| 16 |
+
Err(_) => {
|
| 17 |
+
// println!("gzip解压失败: {}", e);
|
| 18 |
+
None
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
pub trait ToMarkdown {
|
| 24 |
+
fn to_markdown(&self) -> String;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
impl ToMarkdown for BTreeMap<String, String> {
|
| 28 |
+
fn to_markdown(&self) -> String {
|
| 29 |
+
if self.is_empty() {
|
| 30 |
+
return String::new();
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
let mut result = String::from("WebReferences:\n");
|
| 34 |
+
for (i, (url, title)) in self.iter().enumerate() {
|
| 35 |
+
result.push_str(&format!("{}. [{}]({})\n", i + 1, title, url));
|
| 36 |
+
}
|
| 37 |
+
result.push_str("\n");
|
| 38 |
+
result
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
#[derive(PartialEq, Clone, Debug)]
|
| 43 |
+
pub enum StreamMessage {
|
| 44 |
+
// 调试
|
| 45 |
+
Debug(String),
|
| 46 |
+
// 网络引用
|
| 47 |
+
WebReference(BTreeMap<String, String>),
|
| 48 |
+
// 内容开始标志
|
| 49 |
+
ContentStart,
|
| 50 |
+
// 消息内容
|
| 51 |
+
Content(String),
|
| 52 |
+
// 流结束标志
|
| 53 |
+
StreamEnd,
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
impl StreamMessage {
|
| 57 |
+
fn convert_web_ref_to_content(self) -> Self {
|
| 58 |
+
match self {
|
| 59 |
+
StreamMessage::WebReference(refs) => StreamMessage::Content(refs.to_markdown()),
|
| 60 |
+
other => other,
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
pub struct StreamDecoder {
|
| 66 |
+
buffer: Vec<u8>,
|
| 67 |
+
first_result: Option<Vec<StreamMessage>>,
|
| 68 |
+
first_result_ready: bool,
|
| 69 |
+
first_result_taken: bool,
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
impl StreamDecoder {
|
| 73 |
+
pub fn new() -> Self {
|
| 74 |
+
Self {
|
| 75 |
+
buffer: Vec::new(),
|
| 76 |
+
first_result: None,
|
| 77 |
+
first_result_ready: false,
|
| 78 |
+
first_result_taken: false,
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
pub fn take_first_result(&mut self) -> Option<Vec<StreamMessage>> {
|
| 83 |
+
if !self.buffer.is_empty() {
|
| 84 |
+
return None;
|
| 85 |
+
}
|
| 86 |
+
if self.first_result.is_some() {
|
| 87 |
+
self.first_result_taken = true;
|
| 88 |
+
}
|
| 89 |
+
self.first_result.take()
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
#[cfg(test)]
|
| 93 |
+
fn is_incomplete(&self) -> bool {
|
| 94 |
+
!self.buffer.is_empty()
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
pub fn is_first_result_ready(&self) -> bool {
|
| 98 |
+
self.first_result_ready
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
pub fn decode(&mut self, data: &[u8], convert_web_ref: bool) -> Result<Vec<StreamMessage>, StreamError> {
|
| 102 |
+
self.buffer.extend_from_slice(data);
|
| 103 |
+
|
| 104 |
+
if self.buffer.len() < 5 {
|
| 105 |
+
if self.buffer.len() == 0 {
|
| 106 |
+
return Err(StreamError::EmptyStream);
|
| 107 |
+
}
|
| 108 |
+
crate::debug_println!("数据长度小于5字节,当前数据: {}", hex::encode(&self.buffer));
|
| 109 |
+
return Err(StreamError::DataLengthLessThan5);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
let mut messages = Vec::new();
|
| 113 |
+
let mut offset = 0;
|
| 114 |
+
|
| 115 |
+
while offset + 5 <= self.buffer.len() {
|
| 116 |
+
let msg_type = self.buffer[offset];
|
| 117 |
+
let msg_len = u32::from_be_bytes([
|
| 118 |
+
self.buffer[offset + 1],
|
| 119 |
+
self.buffer[offset + 2],
|
| 120 |
+
self.buffer[offset + 3],
|
| 121 |
+
self.buffer[offset + 4],
|
| 122 |
+
]) as usize;
|
| 123 |
+
|
| 124 |
+
if msg_len == 0 {
|
| 125 |
+
offset += 5;
|
| 126 |
+
messages.push(StreamMessage::ContentStart);
|
| 127 |
+
continue;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
if offset + 5 + msg_len > self.buffer.len() {
|
| 131 |
+
break;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
let msg_data = &self.buffer[offset + 5..offset + 5 + msg_len];
|
| 135 |
+
|
| 136 |
+
match self.process_message(msg_type, msg_data)? {
|
| 137 |
+
Some(msg) => {
|
| 138 |
+
if convert_web_ref {
|
| 139 |
+
messages.push(msg.convert_web_ref_to_content());
|
| 140 |
+
} else {
|
| 141 |
+
messages.push(msg);
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
_ => {}
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
offset += 5 + msg_len;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
self.buffer.drain(..offset);
|
| 151 |
+
|
| 152 |
+
if !self.first_result_taken && !messages.is_empty() {
|
| 153 |
+
if self.first_result.is_none() {
|
| 154 |
+
self.first_result = Some(messages.clone());
|
| 155 |
+
} else if !self.first_result_ready {
|
| 156 |
+
self.first_result.as_mut().unwrap().extend(messages.clone());
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
if !self.first_result_ready {
|
| 160 |
+
self.first_result_ready = self.first_result.is_some() && self.buffer.is_empty() && !self.first_result_taken;
|
| 161 |
+
}
|
| 162 |
+
Ok(messages)
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
fn process_message(
|
| 166 |
+
&self,
|
| 167 |
+
msg_type: u8,
|
| 168 |
+
msg_data: &[u8],
|
| 169 |
+
) -> Result<Option<StreamMessage>, StreamError> {
|
| 170 |
+
match msg_type {
|
| 171 |
+
0 => self.handle_text_message(msg_data),
|
| 172 |
+
1 => self.handle_gzip_message(msg_data),
|
| 173 |
+
2 => self.handle_json_message(msg_data),
|
| 174 |
+
3 => self.handle_gzip_json_message(msg_data),
|
| 175 |
+
t => {
|
| 176 |
+
eprintln!("收到未知消息类型: {},请尝试联系开发者以获取支持", t);
|
| 177 |
+
crate::debug_println!("消息类型: {},消息内容: {}", t, hex::encode(msg_data));
|
| 178 |
+
Ok(None)
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
fn handle_text_message(&self, msg_data: &[u8]) -> Result<Option<StreamMessage>, StreamError> {
|
| 184 |
+
if let Ok(response) = StreamChatResponse::decode(msg_data) {
|
| 185 |
+
// crate::debug_println!("[text] StreamChatResponse [hex: {}]: {:?}", hex::encode(msg_data), response);
|
| 186 |
+
if !response.text.is_empty() {
|
| 187 |
+
Ok(Some(StreamMessage::Content(response.text)))
|
| 188 |
+
} else if let Some(filled_prompt) = response.filled_prompt {
|
| 189 |
+
Ok(Some(StreamMessage::Debug(filled_prompt)))
|
| 190 |
+
} else if let Some(web_citation) = response.web_citation {
|
| 191 |
+
let mut refs = BTreeMap::new();
|
| 192 |
+
for reference in web_citation.references {
|
| 193 |
+
refs.insert(reference.url, reference.title);
|
| 194 |
+
}
|
| 195 |
+
Ok(Some(StreamMessage::WebReference(refs)))
|
| 196 |
+
} else {
|
| 197 |
+
Ok(None)
|
| 198 |
+
}
|
| 199 |
+
} else {
|
| 200 |
+
Ok(None)
|
| 201 |
+
}
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
fn handle_gzip_message(&self, msg_data: &[u8]) -> Result<Option<StreamMessage>, StreamError> {
|
| 205 |
+
if let Some(text) = decompress_gzip(msg_data) {
|
| 206 |
+
if let Ok(response) = StreamChatResponse::decode(&text[..]) {
|
| 207 |
+
// crate::debug_println!("[gzip] StreamChatResponse [hex: {}]: {:?}", hex::encode(msg_data), response);
|
| 208 |
+
if !response.text.is_empty() {
|
| 209 |
+
Ok(Some(StreamMessage::Content(response.text)))
|
| 210 |
+
} else if let Some(filled_prompt) = response.filled_prompt {
|
| 211 |
+
Ok(Some(StreamMessage::Debug(filled_prompt)))
|
| 212 |
+
} else if let Some(web_citation) = response.web_citation {
|
| 213 |
+
let mut refs = BTreeMap::new();
|
| 214 |
+
for reference in web_citation.references {
|
| 215 |
+
refs.insert(reference.url, reference.title);
|
| 216 |
+
}
|
| 217 |
+
Ok(Some(StreamMessage::WebReference(refs)))
|
| 218 |
+
} else {
|
| 219 |
+
Ok(None)
|
| 220 |
+
}
|
| 221 |
+
} else {
|
| 222 |
+
Ok(None)
|
| 223 |
+
}
|
| 224 |
+
} else {
|
| 225 |
+
Ok(None)
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
fn handle_json_message(&self, msg_data: &[u8]) -> Result<Option<StreamMessage>, StreamError> {
|
| 230 |
+
if msg_data.len() == 2 {
|
| 231 |
+
return Ok(Some(StreamMessage::StreamEnd));
|
| 232 |
+
}
|
| 233 |
+
if let Ok(text) = String::from_utf8(msg_data.to_vec()) {
|
| 234 |
+
// println!("JSON消息: {}", text);
|
| 235 |
+
if let Ok(error) = serde_json::from_str::<ChatError>(&text) {
|
| 236 |
+
return Err(StreamError::ChatError(error));
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
Ok(None)
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
fn handle_gzip_json_message(
|
| 243 |
+
&self,
|
| 244 |
+
msg_data: &[u8],
|
| 245 |
+
) -> Result<Option<StreamMessage>, StreamError> {
|
| 246 |
+
if let Some(text) = decompress_gzip(msg_data) {
|
| 247 |
+
if text.len() == 2 {
|
| 248 |
+
return Ok(Some(StreamMessage::StreamEnd));
|
| 249 |
+
}
|
| 250 |
+
if let Ok(text) = String::from_utf8(text) {
|
| 251 |
+
// println!("JSON消息: {}", text);
|
| 252 |
+
if let Ok(error) = serde_json::from_str::<ChatError>(&text) {
|
| 253 |
+
return Err(StreamError::ChatError(error));
|
| 254 |
+
}
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
Ok(None)
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
#[cfg(test)]
|
| 262 |
+
mod tests {
|
| 263 |
+
use super::*;
|
| 264 |
+
|
| 265 |
+
#[test]
|
| 266 |
+
fn test_single_chunk() {
|
| 267 |
+
// 使用include_str!加载测试数据文件
|
| 268 |
+
let stream_data = include_str!("../../../tests/data/stream_data.txt");
|
| 269 |
+
|
| 270 |
+
// 将整个字符串按每两个字符分割成字节
|
| 271 |
+
let bytes: Vec<u8> = stream_data
|
| 272 |
+
.as_bytes()
|
| 273 |
+
.chunks(2)
|
| 274 |
+
.map(|chunk| {
|
| 275 |
+
let hex_str = std::str::from_utf8(chunk).unwrap();
|
| 276 |
+
u8::from_str_radix(hex_str, 16).unwrap()
|
| 277 |
+
})
|
| 278 |
+
.collect();
|
| 279 |
+
|
| 280 |
+
// 创建解码器
|
| 281 |
+
let mut decoder = StreamDecoder::new();
|
| 282 |
+
|
| 283 |
+
match decoder.decode(&bytes, false) {
|
| 284 |
+
Ok(messages) => {
|
| 285 |
+
for message in messages {
|
| 286 |
+
match message {
|
| 287 |
+
StreamMessage::StreamEnd => {
|
| 288 |
+
println!("流结束");
|
| 289 |
+
break;
|
| 290 |
+
}
|
| 291 |
+
StreamMessage::Content(msg) => {
|
| 292 |
+
println!("消息内容: {}", msg);
|
| 293 |
+
}
|
| 294 |
+
StreamMessage::WebReference(refs) => {
|
| 295 |
+
println!("网页引用:");
|
| 296 |
+
for (i, (url, title)) in refs.iter().enumerate() {
|
| 297 |
+
println!("{}. {} - {}", i, url, title);
|
| 298 |
+
}
|
| 299 |
+
}
|
| 300 |
+
StreamMessage::Debug(prompt) => {
|
| 301 |
+
println!("调试信息: {}", prompt);
|
| 302 |
+
}
|
| 303 |
+
StreamMessage::ContentStart => {
|
| 304 |
+
println!("流开始");
|
| 305 |
+
}
|
| 306 |
+
}
|
| 307 |
+
}
|
| 308 |
+
}
|
| 309 |
+
Err(e) => {
|
| 310 |
+
println!("解析错误: {}", e);
|
| 311 |
+
}
|
| 312 |
+
}
|
| 313 |
+
if decoder.is_incomplete() {
|
| 314 |
+
println!("数据不完整");
|
| 315 |
+
}
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
#[test]
|
| 319 |
+
fn test_multiple_chunks() {
|
| 320 |
+
// 使用include_str!加载测试数据文件
|
| 321 |
+
let stream_data = include_str!("../../../tests/data/stream_data.txt");
|
| 322 |
+
|
| 323 |
+
// 将整个字符串按每两个字符分割成字节
|
| 324 |
+
let bytes: Vec<u8> = stream_data
|
| 325 |
+
.as_bytes()
|
| 326 |
+
.chunks(2)
|
| 327 |
+
.map(|chunk| {
|
| 328 |
+
let hex_str = std::str::from_utf8(chunk).unwrap();
|
| 329 |
+
u8::from_str_radix(hex_str, 16).unwrap()
|
| 330 |
+
})
|
| 331 |
+
.collect();
|
| 332 |
+
|
| 333 |
+
// 创建解码器
|
| 334 |
+
let mut decoder = StreamDecoder::new();
|
| 335 |
+
|
| 336 |
+
// 辅助函数:找到下一个消息边界
|
| 337 |
+
fn find_next_message_boundary(bytes: &[u8]) -> usize {
|
| 338 |
+
if bytes.len() < 5 {
|
| 339 |
+
return bytes.len();
|
| 340 |
+
}
|
| 341 |
+
let msg_len = u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize;
|
| 342 |
+
5 + msg_len
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
// 辅助函数:将字节转换为hex字符串
|
| 346 |
+
fn bytes_to_hex(bytes: &[u8]) -> String {
|
| 347 |
+
bytes
|
| 348 |
+
.iter()
|
| 349 |
+
.map(|b| format!("{:02X}", b))
|
| 350 |
+
.collect::<Vec<String>>()
|
| 351 |
+
.join("")
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
// 多次解析数据
|
| 355 |
+
let mut offset = 0;
|
| 356 |
+
let mut should_break = false;
|
| 357 |
+
|
| 358 |
+
while offset < bytes.len() {
|
| 359 |
+
let remaining_bytes = &bytes[offset..];
|
| 360 |
+
let msg_boundary = find_next_message_boundary(remaining_bytes);
|
| 361 |
+
let current_msg_bytes = &remaining_bytes[..msg_boundary];
|
| 362 |
+
let hex_str = bytes_to_hex(current_msg_bytes);
|
| 363 |
+
|
| 364 |
+
match decoder.decode(current_msg_bytes, false) {
|
| 365 |
+
Ok(messages) => {
|
| 366 |
+
for message in messages {
|
| 367 |
+
match message {
|
| 368 |
+
StreamMessage::StreamEnd => {
|
| 369 |
+
println!("流结束 [hex: {}]", hex_str);
|
| 370 |
+
should_break = true;
|
| 371 |
+
break;
|
| 372 |
+
}
|
| 373 |
+
StreamMessage::Content(msg) => {
|
| 374 |
+
println!("消息内容 [hex: {}]: {}", hex_str, msg);
|
| 375 |
+
}
|
| 376 |
+
StreamMessage::WebReference(refs) => {
|
| 377 |
+
println!("网页引用 [hex: {}]:", hex_str);
|
| 378 |
+
for (i, (url, title)) in refs.iter().enumerate() {
|
| 379 |
+
println!("{}. {} - {}", i, url, title);
|
| 380 |
+
}
|
| 381 |
+
}
|
| 382 |
+
StreamMessage::Debug(prompt) => {
|
| 383 |
+
println!("调试信息 [hex: {}]: {}", hex_str, prompt);
|
| 384 |
+
}
|
| 385 |
+
StreamMessage::ContentStart => {
|
| 386 |
+
println!("流开始 [hex: {}]", hex_str);
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
}
|
| 390 |
+
if should_break {
|
| 391 |
+
break;
|
| 392 |
+
}
|
| 393 |
+
if decoder.is_incomplete() {
|
| 394 |
+
println!("数据不完整 [hex: {}]", hex_str);
|
| 395 |
+
break;
|
| 396 |
+
}
|
| 397 |
+
offset += msg_boundary;
|
| 398 |
+
}
|
| 399 |
+
Err(e) => {
|
| 400 |
+
println!("解析错误 [hex: {}]: {}", hex_str, e);
|
| 401 |
+
break;
|
| 402 |
+
}
|
| 403 |
+
}
|
| 404 |
+
}
|
| 405 |
+
}
|
| 406 |
+
}
|