Skip to content

fix(layout): allow configuring a keyless self-hosted provider - #1062

Open
ttxl314 wants to merge 2 commits into
poco-ai:mainfrom
ttxl314:fix/layout-selfhosted-keyless
Open

ttxl314 wants to merge 2 commits into
poco-ai:mainfrom
ttxl314:fix/layout-selfhosted-keyless

Conversation

@ttxl314

@ttxl314 ttxl314 commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

摘要

设置页里一个服务商只有填了 API Key 才算「已配置」,所以无鉴权的自建 MinerU 永远进不了上方「解析方式 / 正文解析引擎」下拉:配置存得下、探针不跑、点「保存」没有任何反馈。本 PR 用统一的就绪判定(有 key,或自建 + Base URL)同时决定下拉是否可选与是否探针,并让探针失败把 Host 的原因显示出来。

Closes #1060

类型

  • fix:修复

场景

docs/frontend/pdf-layout-analysis.md:159 已经写明「自建 4.0 的 Token 由服务端 --api-key 决定(匿名部署可留空)」,但配置页把「有 key」等同于「已配置」:isProviderConfigured() 与卡片里的 configured 都只判 apiKey.trim().length > 0,persistConfig() 在 key 为空时直接 early-return,探针因此从不执行。

后果就是 #1060 的复现:Base URL 指向 http://127.0.0.1:8012、打开「自建 API」、Key 留空 → 点按钮无反应,下拉里没有 MinerU;只能随便填一个字符强行通过,而那个假值会进入 Authorization 头。

Host 侧本来不需要这个限制:resolve_target() 在 self_hosted 且无 key 时返回空 auth,authed() 在 auth 为空时不发 Authorization。所以本 PR 是纯前端 + 文案,Rust 零改动。

顺带补一个发现性问题:下拉为空时界面没有任何解释,用户会以为「这个 app 不支持 MinerU」。

主要改动

文件 改动
src/lib/pdf/layout/providers.ts 新增 isProviderReady(card, config)(有 key,或自建 + Base URL)与 hasReadyProvider(cards, configs)
src/lib/pdf/layout/provider-config.ts persistLayoutProviderConfig({ ready }),不传时保持「key 非空」旧语义;probeLayoutProvider 返回 { ok, error }
src/components/settings/panes/layout-pane.tsx 三处判定统一到 isProviderReady;探针失败弹 toast 显示 Host 原因;下拉下方条件提示
src/components/onboarding/steps/layout-step.tsx 适配探针返回值;引导页两张卡片下方补一句「自建请稍后在设置中配置」
src/i18n/locales/{zh-CN,en}/settings.json 「确定」→「保存」;新增 layout.menusHint
src/i18n/locales/{zh-CN,en}/onboarding.json 新增 layout.selfHostedHint
test/layout-provider-ready.test.ts 新增 9 条用例(isProviderReady 6 条 + hasReadyProvider 3 条)

判定顺序是刻意的:有 key 即就绪(云服务行为完全不变)→ 无 key 才看自建开关 → 自建还必须给得出 base URL,因为 resolve_target() 只认 base_url,没地址一定失败。

提示的显示条件是「一个远程服务商都没就绪」:它是冷启动引导,用户配好任意一个之后就已经掌握了「先配、再保存、才会出现」这条规则,届时收起避免常驻噪音。「配了 Paddle 但 MinerU 仍未配」这一情形不提示,属于有意取舍,并由单测锁定。

截图(UI / 功能改动必填)

截图 说明
之前 01-之前-自建开启但下拉里没有MinerU 自建 API 开启、API Key 留空:两个下拉都只有本地,MinerU 不在列表;按钮为「确定」
之后 02-之后-空key保存成功-灯绿且下拉可选MinerU 同样留空 Key,点「保存」后小灯变绿,「正文解析引擎」可直接选到 MinerU
之后 03-之后-下拉为空时的引导提示 没有任何服务商就绪时,下拉下方给出引导:先配置并保存,检测可用后即可选择
之后 04-之后-探针失败显示具体原因 探针失败时显示具体原因(answered neither /v1/health (4.x) nor /health (3.x)),而不是笼统的「不可用」
之前 05-之前-引导流程无提示 引导步骤两张卡片下方没有提示
之后 06-之后-引导流程提示自建去设置里配 引导步骤下方提示:自建请稍后在「设置 → 版面解析」中配置

备注:「之前」的引导截图取自 v0.15.3,该步骤标题已由 1f4e2072 feat(onboarding): rename the formula and figure parsing step 更新,与本 PR 无关。

Demo(feat 必填)

不适用(fix)。

自查清单

  • 本地 pre-commit 通过(husky → lint-staged → biome check --write,未使用 --no-verify)
  • 已运行与改动对应的检查:pnpm typecheck、pnpm exec biome check、pnpm test(406 文件通过;settings-window-boot 在全量并行时超时,单独运行通过,属既有 flaky)
  • UI 文案已走 t() 并同步 zh-CN
  • 手动验证(真实自建 MinerU 4.1.7,http://127.0.0.1:8012):
    • 自建 API 开、API Key 留空、点「保存」→ 小灯变绿
    • 上方「解析方式」「正文解析引擎」均出现 MinerU
    • 落盘 settings.json:apiKey=""、selfHosted=true、baseUrl=http://127.0.0.1:8012,重启后仍有效
    • 反向:Base URL 改成 https://mineru.net 且自建开 → 探针 409 ms 失败,错误为 MinerU self-hosted server https://mineru.net answered neither /v1/health (4.x) nor /health (3.x)
  • CI 全绿
  • 相关文档已更新(pdf-layout-analysis.md:159 已写明匿名自建可留空,本 PR 未改文档)

范围外(不在本 PR)

  • Rust 侧:resolve_target / authed 已支持无鉴权自建,未改动。
  • 引导流程:无「自建 API」开关,仍要求 API Key;本 PR 只补了一句指向设置页的提示。
  • 探针失败触发全局代理提醒:notifyError 的既有副作用,localhost 连不上时会误报,另开跟进。
  • 下拉里列出「未配置」的禁用项:属于 「自建 API」已开启仍强制 API Key,且「确定」静默失败无提示 #1060 中门禁强度的设计讨论。

关于额外的 chore(format) 提交

TypeScript (quality) 在主干上自 2026-11-10 起就因 scripts/rewrite-updater-json.mjs 的格式化问题而失败(基点 e8b4b510 的 CI run 同样失败在同一处)。为让本分支的检查能通过,附带提交 addbb3a5 做了纯格式化修复(2 处换行,无行为变化);如不希望混入无关改动,可以单独拆出去。

A provider card counted as "configured" only when an API key was present,
so a self-hosted MinerU endpoint that runs anonymously could never reach the
backend selects: the config persisted, the probe never ran, and Save failed
silently with no feedback.

Gate the backend selects and the probe on a shared readiness check instead:
a key, or - for providers that expose the self-hosted option - a self-hosted
endpoint with a base URL. The Host already supports that case
(resolve_target leaves the Authorization header empty when no key is stored),
so this stays a frontend change.

Probe failures now carry the Host message, so a rejected token or an
unreachable endpoint no longer hides behind a silent grey dot.

Also name the button Save instead of Confirm (it commits an inline form, not
a dialog), explain the ordering where users look first - the backend menus
only list configured providers - and point self-hosted users at Settings
from the onboarding step.

Refs poco-ai#1060
`biome ci .` has been failing on main since 2026-11-10 because two lines in
scripts/rewrite-updater-json.mjs exceed the formatter width (the file was last
touched by 46c6155 and never re-formatted). Formatting only, no behaviour
change.

Kept as a separate commit so it can be dropped if you would rather fix it on
its own.
@ttxl314
ttxl314 marked this pull request as ready for review November 10, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

「自建 API」已开启仍强制 API Key,且「确定」静默失败无提示

1 participant