Conversation
NyxID's /api/v1/docs/openapi.json is human-only (rejects service-account and delegated tokens), so unauthenticated fetches always return 401. Production currently never wires SpecFetchToken, so the catalog churns 30-min 401 retries and stays empty — silently disabling nyxid_proxy and nyxid_search_capabilities. - Wire Aevatar:NyxId:SpecFetchToken in MainnetHostBuilderExtensions - Skip the background refresh entirely when the token is missing - Refresh the SpecFetchToken XML doc to drop the stale "public endpoint" claim - Cover the new behavior with NyxIdSpecCatalogTests Refs #522. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## dev #523 +/- ##
==========================================
- Coverage 71.66% 71.65% -0.01%
==========================================
Files 1240 1240
Lines 89709 89712 +3
Branches 11733 11733
==========================================
- Hits 64287 64285 -2
- Misses 20821 20822 +1
- Partials 4601 4605 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 4 files with indirect coverage changes 🚀 New features to boost your workflow:
|
| /// as human-only (rejects service-account and delegated tokens), so this | ||
| /// must be a real user's API key or access token. When unset the catalog | ||
| /// stays empty and the background refresh is skipped — generic capability | ||
| /// discovery (<c>nyxid_search_capabilities</c>, <c>nyxid_proxy</c>) is |
There was a problem hiding this comment.
这里应该写 nyxid_proxy_execute,不是 nyxid_proxy。nyxid_proxy 直接按 slug/path 走代理,不依赖 NyxIdSpecCatalog;catalog 为空时真正不可用的是 operation_id 查找/执行链路(nyxid_search_capabilities -> nyxid_proxy_execute)。当前注释会误导排障,把直接代理工具也归为不可用。
|
|
||
| using var catalog = new NyxIdSpecCatalog(options, http); | ||
|
|
||
| await handler.FirstRequestReceived.Task; |
There was a problem hiding this comment.
这个 await 没有超时。如果以后 constructor 回归为不触发 initial fetch,测试不会失败而是挂住 CI。建议用 await handler.FirstRequestReceived.Task.WaitAsync(TimeSpan.FromSeconds(2)); 之类的有界等待,并继续断言 header。
问题
生产
aismart-app-mainnet/aevatar-console-backend日志中,NyxIdSpecCatalog每 30 分钟刷新一次,每次 3 次重试全部 401,catalog 始终为空,导致:nyxid_proxyLLM tool 完全不可用(任何operation_id查找失败)nyxid_search_capabilities走"catalog is empty" fallback 分支,本质失效详细调研见 #522。
根因
/api/v1/docs/openapi.json在api_v1_human_only路由组,挂了reject_service_account_tokens+reject_delegated_tokens,只接受真人用户 JWT 或 API key,不再是公开端点。MainnetHostBuilderExtensions.AddNyxIdTools调用只绑了BaseUrl,完全没有绑SpecFetchToken;catalog 因此发不带Authorization的请求 → 401。NyxIdToolOptions.SpecFetchToken的 XML doc 注释还停留在"public endpoint"假设,已经过时。改动
src/Aevatar.Mainnet.Host.Api/Hosting/MainnetHostBuilderExtensions.csAddNyxIdTools中绑定Aevatar:NyxId:SpecFetchTokensrc/Aevatar.AI.ToolProviders.NyxId/NyxIdSpecCatalog.csSpecFetchToken为空/空白,跳过 initial fetch + 定时器,记 Information 日志;FetchAndUpdateAsync总是带 Bearer(删除冗余空判)src/Aevatar.AI.ToolProviders.NyxId/NyxIdToolOptions.csSpecFetchTokenXML doc,去掉 "public endpoint" 表述,明确 human-only 约束test/Aevatar.AI.Tests/NyxIdSpecCatalogTests.cs验证
部署侧 follow-up(不在本 PR 范围)
合 PR 之后还需要 ops/SRE:
appsettings.*.json里通过Aevatar:NyxId:SpecFetchToken把这个 key 注入 console-backend。NyxIdSpecCatalog updated: N operations(成功)或者SpecFetchToken not configured; skipping...(仍未配置)NyxIdSpecCatalog refresh attempt N/3 failed长期方案(NyxID 侧)
human-only 这一约束让 catalog 不得不绑某个真实用户。更干净的做法是 NyxID 把 spec endpoint 移出
human_only,允许 service account token,或者出一个无鉴权的"catalog only / 不含敏感字段"的子集 spec 端点。这块留给 NyxID 团队评估,不在本 PR 范围。关联