feat: add remote path mappings for split PMHQ deployments#784
Open
kkkzbh wants to merge 2 commits into
Open
Conversation
Contributor
审阅者指南引入可配置的远程路径到本地路径映射层,并将其整合到 NTQQ 文件/媒体处理的各个环节中,使 LLBot 在 PMHQ 运行于独立文件系统命名空间时仍能正常工作,同时在协议载荷中保留远程路径。 使用远程路径映射进行媒体上传的时序图sequenceDiagram
actor User
participant SendElement as SendElement.Video
participant NTFileApi as NTQQFileApi
participant PathMap as PathMappingUtils
participant PMHQ
participant FS as FileSystem
User->>SendElement: sendVideo(filePath)
SendElement->>NTFileApi: uploadFile(filePath, Video)
NTFileApi->>PMHQ: getRichMediaFilePath(md5, fileName, type, subType)
PMHQ-->>NTFileApi: mediaPath (remote)
NTFileApi->>PathMap: remotePathToLocal(mediaPath)
PathMap-->>NTFileApi: localMediaPath
NTFileApi->>FS: copyFile(filePath, localMediaPath)
FS-->>NTFileApi: ok
NTFileApi-->>SendElement: { path: mediaPath, localPath: localMediaPath }
SendElement->>FS: getVideoInfo(localPath)
SendElement->>PathMap: remotePathToLocal(thumbFilePath)
PathMap-->>SendElement: localThumbFilePath
SendElement->>FS: mkdir(dirname(localThumbFilePath))
SendElement->>FS: copyFile(thumbSource, localThumbFilePath)
SendElement->>FS: stat(localThumbFilePath), getMd5HexFromFile(localThumbFilePath)
文件级改动
可能关联的问题
提示与命令与 Sourcery 交互
自定义你的使用体验打开你的 仪表盘 以:
获取帮助Original review guide in EnglishReviewer's GuideIntroduce a configurable remote-to-local path mapping layer and integrate it across NTQQ file/media handling so LLBot can operate correctly when PMHQ runs in a separate filesystem namespace, while preserving remote paths in protocol payloads. Sequence diagram for media upload using remote path mappingssequenceDiagram
actor User
participant SendElement as SendElement.Video
participant NTFileApi as NTQQFileApi
participant PathMap as PathMappingUtils
participant PMHQ
participant FS as FileSystem
User->>SendElement: sendVideo(filePath)
SendElement->>NTFileApi: uploadFile(filePath, Video)
NTFileApi->>PMHQ: getRichMediaFilePath(md5, fileName, type, subType)
PMHQ-->>NTFileApi: mediaPath (remote)
NTFileApi->>PathMap: remotePathToLocal(mediaPath)
PathMap-->>NTFileApi: localMediaPath
NTFileApi->>FS: copyFile(filePath, localMediaPath)
FS-->>NTFileApi: ok
NTFileApi-->>SendElement: { path: mediaPath, localPath: localMediaPath }
SendElement->>FS: getVideoInfo(localPath)
SendElement->>PathMap: remotePathToLocal(thumbFilePath)
PathMap-->>SendElement: localThumbFilePath
SendElement->>FS: mkdir(dirname(localThumbFilePath))
SendElement->>FS: copyFile(thumbSource, localThumbFilePath)
SendElement->>FS: stat(localThumbFilePath), getMd5HexFromFile(localThumbFilePath)
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - 我发现了 1 个问题,并留下了一些总体反馈:
RemotePathMapping.name字段在类型定义中是可选的,但在SetConfigAction的 schema 中被当作必填字段;建议在 schema 中也将其设置为可选,以保持运行时校验与类型定义及 JSON 配置示例的一致性。- 在
NTQQFileApi中,remotePathMappings在setRemotePathMappings里会被归一化一次,但remotePathToLocal/localPathToRemote会将它们传给mapRemotePathToLocal/mapLocalPathToRemote,而这些函数会重新调用normalizeRemotePathMappings;建议增加接受已归一化映射的变体,以避免每次调用时重复归一化。
给 AI Agents 的提示词
Please address the comments from this code review:
## Overall Comments
- The `RemotePathMapping.name` field is typed as optional but enforced as required in the `SetConfigAction` schema; consider making it optional in the schema as well to keep runtime validation consistent with the type definition and the JSON config example.
- In `NTQQFileApi`, `remotePathMappings` are normalized once in `setRemotePathMappings`, but `remotePathToLocal`/`localPathToRemote` pass them to `mapRemotePathToLocal`/`mapLocalPathToRemote`, which re-run `normalizeRemotePathMappings`; consider adding variants that accept pre-normalized mappings to avoid repeated normalization on each call.
## Individual Comments
### Comment 1
<location path="src/onebot11/action/llbot/system/Config.ts" line_range="28-34" />
<code_context>
ffmpeg: Schema.string(),
musicSignUrl: Schema.string(),
msgCacheExpire: Schema.number(),
+ remotePathMappings: Schema.array(Schema.object({
+ name: Schema.string(),
+ remotePrefix: Schema.string(),
+ localPrefix: Schema.string(),
+ remoteStyle: Schema.union(['posix', 'win32']),
+ localStyle: Schema.union(['posix', 'win32']),
+ })),
rawMsgPB: Schema.boolean()
</code_context>
<issue_to_address>
**suggestion (bug_risk):** `RemotePathMapping.name` is typed as optional but required by the schema, which may cause confusion.
In `common/types.ts`, `RemotePathMapping` has `name?: string`, but this schema requires `name: Schema.string()`. That lets TS callers omit `name` while config validation will reject it. Please either make `name` required in the type or mark it optional in the schema (e.g. `Schema.string().optional()`), so the type and schema stay consistent.
```suggestion
remotePathMappings: Schema.array(Schema.object({
name: Schema.string().optional(),
remotePrefix: Schema.string(),
localPrefix: Schema.string(),
remoteStyle: Schema.union(['posix', 'win32']),
localStyle: Schema.union(['posix', 'win32']),
})),
```
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的 review。
Original comment in English
Hey - I've found 1 issue, and left some high level feedback:
- The
RemotePathMapping.namefield is typed as optional but enforced as required in theSetConfigActionschema; consider making it optional in the schema as well to keep runtime validation consistent with the type definition and the JSON config example. - In
NTQQFileApi,remotePathMappingsare normalized once insetRemotePathMappings, butremotePathToLocal/localPathToRemotepass them tomapRemotePathToLocal/mapLocalPathToRemote, which re-runnormalizeRemotePathMappings; consider adding variants that accept pre-normalized mappings to avoid repeated normalization on each call.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `RemotePathMapping.name` field is typed as optional but enforced as required in the `SetConfigAction` schema; consider making it optional in the schema as well to keep runtime validation consistent with the type definition and the JSON config example.
- In `NTQQFileApi`, `remotePathMappings` are normalized once in `setRemotePathMappings`, but `remotePathToLocal`/`localPathToRemote` pass them to `mapRemotePathToLocal`/`mapLocalPathToRemote`, which re-run `normalizeRemotePathMappings`; consider adding variants that accept pre-normalized mappings to avoid repeated normalization on each call.
## Individual Comments
### Comment 1
<location path="src/onebot11/action/llbot/system/Config.ts" line_range="28-34" />
<code_context>
ffmpeg: Schema.string(),
musicSignUrl: Schema.string(),
msgCacheExpire: Schema.number(),
+ remotePathMappings: Schema.array(Schema.object({
+ name: Schema.string(),
+ remotePrefix: Schema.string(),
+ localPrefix: Schema.string(),
+ remoteStyle: Schema.union(['posix', 'win32']),
+ localStyle: Schema.union(['posix', 'win32']),
+ })),
rawMsgPB: Schema.boolean()
</code_context>
<issue_to_address>
**suggestion (bug_risk):** `RemotePathMapping.name` is typed as optional but required by the schema, which may cause confusion.
In `common/types.ts`, `RemotePathMapping` has `name?: string`, but this schema requires `name: Schema.string()`. That lets TS callers omit `name` while config validation will reject it. Please either make `name` required in the type or mark it optional in the schema (e.g. `Schema.string().optional()`), so the type and schema stay consistent.
```suggestion
remotePathMappings: Schema.array(Schema.object({
name: Schema.string().optional(),
remotePrefix: Schema.string(),
localPrefix: Schema.string(),
remoteStyle: Schema.union(['posix', 'win32']),
localStyle: Schema.union(['posix', 'win32']),
})),
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
这个 PR 增加了一个通用的
remotePathMappings配置,用于支持 LLBot 进程和 PMHQ/QQ 运行在不同文件系统命名空间时的媒体路径映射。典型场景是:PMHQ 运行在容器中,返回
/root/.config/QQ/...这类容器内路径;LLBot 运行在宿主机进程中,直接对该路径执行copyFile、stat、createReadStream会失败。现在 LLBot 可以通过显式配置把 PMHQ 返回的远端路径映射到 LLBot 本地可访问路径,同时在消息元素中继续保留 PMHQ/QQ 需要的原始路径。Motivation
我们在部署中采用了“LLBot 宿主机进程 + PMHQ 容器”的运行方式。
这样做的背景是:早期 LLBot 和 PMHQ 都运行在 Podman 容器中,但实际运维中遇到过一些和业务无关的容器层问题,例如:
pmhq:13000。podman-compose/ rootless Podman 下网络附着、服务别名、host loopback 暴露和部署验证比较容易产生不确定性。127.0.0.1:3001,LLBot 也可以直接连接 PMHQ 暴露到宿主机的127.0.0.1:${PMHQ_PORT},整体运行边界更清晰。同时,PMHQ 仍然适合保留在容器中,因为它承载的是 QQ 客户端运行时、登录态、Xvfb/图形环境和 QQ 数据目录。这个部分更需要容器隔离,也更适合独立重启和排障。
这个运行方式清理了网络和部署层的不确定性,但也暴露出一个新的边界:LLBot 和 PMHQ 不再共享同一个文件系统命名空间。PMHQ 返回的
/root/.config/QQ/...对 PMHQ 容器是正确路径,但对宿主机 LLBot 进程不是可直接访问路径。因此,这个 PR 的目标不是为某个部署脚本写死特殊逻辑,而是让 LLBot 正式支持“远端运行时路径”和“本地进程路径”不同的部署拓扑。
Design
这个 PR 采用显式配置,而不是自动探测容器环境。
新增配置示例:
{ "remotePathMappings": [ { "name": "pmhq-qq-root", "remotePrefix": "/root/.config/QQ", "localPrefix": "/var/lib/containers/storage/volumes/qqbot-stack_qq_volume/_data", "remoteStyle": "posix", "localStyle": "posix" } ] }设计原则:
remotePrefix和localPrefix的实际值。remotePathMappings: [],未配置时保持现有同命名空间行为。posix和win32路径风格。/root/.config/QQ2的路径。Changes
remotePathMappings顶层配置。NTQQFileApi.uploadFile()复制文件时使用本地映射路径,但返回的消息路径保持 PMHQ 原路径。set_configschema 补充remotePathMappings字段。Why this belongs in LLBot
PMHQ 返回的是它自身运行环境中的 QQ 数据目录路径,这对 PMHQ 来说是合理的。真正同时需要理解“PMHQ 返回路径”和“LLBot 本地可访问路径”的组件是 LLBot,因为 LLBot 会:
stat、hash、缩略图生成、createReadStream等本地操作。因此,这个能力放在 LLBot 的文件 API 层更合适。它把部署拓扑中的路径边界显式建模,而不是要求每个部署项目去修改打包后的
llbot.js或在外部做文本 patch。Compatibility
默认配置为空数组,因此现有 LLBot 和 PMHQ 运行在同一文件系统命名空间的部署方式不会受到影响。
对于容器化 PMHQ + 宿主机 LLBot 的部署,部署系统可以在生成配置时写入合适的映射。这个 PR 不假设具体容器运行时,也不依赖 Podman/Docker API。
Testing
npx vitest run --config vitest.config.ts test/unitnpx vite buildSummary by Sourcery
添加可配置的远程到本地路径映射,并在 NTQQ 文件/媒体处理链路中统一应用,以便在 PMHQ 运行于独立文件系统命名空间时,LLBot 仍能正常工作。
新功能:
remotePathMappings,支持 POSIX 和 Windows 路径风格,用于将远程 QQ/PMHQ 路径映射到本地可访问路径。增强改进:
NTQQFileApi内对所有媒体上传、哈希计算、流式传输、缩略图生成以及自动文件清理流程中规范化并应用远程/本地路径映射,同时在消息负载中保留原始远程路径。remotePathMappings,并新增共享的pathMapping工具,用于双向路径转换,供核心消息处理逻辑和 WebUI 代理路由共用。测试:
NTQQFileApi的路径映射行为以及通用的pathMapping工具添加单元测试。Original summary in English
Summary by Sourcery
Add configurable remote-to-local path mappings and apply them across NTQQ file/media handling so LLBot can operate when PMHQ runs in a separate filesystem namespace.
New Features:
Enhancements:
Tests:
新功能:
remotePathMappings配置,支持 POSIX 和 Windows 风格的路径前缀,并支持最长前缀匹配。改进:
NTQQFileApi中,使所有媒体上传、哈希计算、流式传输、缩略图生成、删除以及 WebUI/Satori 代理操作都使用本地可访问路径,同时在消息负载中保留远程路径。remotePathMappings,并添加共享的pathMapping工具,用于双向路径转换。测试:
NTQQFileApi的路径映射行为以及通用的pathMapping工具添加单元测试。Original summary in English
Summary by Sourcery
添加可配置的远程到本地路径映射,并在 NTQQ 文件/媒体处理链路中统一应用,以便在 PMHQ 运行于独立文件系统命名空间时,LLBot 仍能正常工作。
新功能:
remotePathMappings,支持 POSIX 和 Windows 路径风格,用于将远程 QQ/PMHQ 路径映射到本地可访问路径。增强改进:
NTQQFileApi内对所有媒体上传、哈希计算、流式传输、缩略图生成以及自动文件清理流程中规范化并应用远程/本地路径映射,同时在消息负载中保留原始远程路径。remotePathMappings,并新增共享的pathMapping工具,用于双向路径转换,供核心消息处理逻辑和 WebUI 代理路由共用。测试:
NTQQFileApi的路径映射行为以及通用的pathMapping工具添加单元测试。Original summary in English
Summary by Sourcery
Add configurable remote-to-local path mappings and apply them across NTQQ file/media handling so LLBot can operate when PMHQ runs in a separate filesystem namespace.
New Features:
Enhancements:
Tests: