Skip to content

修复定时渠道测试用文本 chat 接口探测纯视频渠道导致误禁用 - #678

Open
think-back wants to merge 1 commit into
mainfrom
fix/video-channel-test-endpoint
Open

修复定时渠道测试用文本 chat 接口探测纯视频渠道导致误禁用#678
think-back wants to merge 1 commit into
mainfrom
fix/video-channel-test-endpoint

Conversation

@think-back

Copy link
Copy Markdown
Collaborator

问题

定时渠道测试(每 20 分钟一轮)会向只提供异步视频生成的渠道发 POST /v1/chat/completions。这些上游没有 chat 接口,每次探测必然报错 → ShouldDisableChannel 判定为渠道级故障 → 自动禁用一个本来健康的渠道。

生产实证(newapi-console,渠道 #120「BytePlus Seedance 2.0」,type=107):

channel test bad response: channel_id=120 name=BytePlus Seedance 2.0 type=107
  model=ep-20260805160143-dkmk9 endpoint_type=  status=401
  err=bad response status code 401, message: The API key format is incorrect.

注意 endpoint_type= 是空的,测试模型还取了 channel.GetModels()[0](一个 endpoint id)。渠道被禁用后,用户侧表现为 分组 Seedance2.0 Official 下模型 seedance-2.0-fast 的可用渠道不存在

受影响渠道类型:BytePlus(107)、TechMobiVideo(105)、XaiGrokVideo(108)、Sonilo(109)、MiniMaxH3(110)、Sora(55)、BlockRunVideo(101)、BlockRunSeedance(102)。

根因

端点注册表 GetEndpointTypesByChannelType 已经把这些渠道声明为 EndpointTypeOpenAIVideocommon/endpoint_type.go:31-46),但 testChannelWithOptions 从来没查过它:normalizeChannelTestEndpoint 对这些类型返回 "",于是 requestPath 落到默认的 /v1/chat/completions

为什么不是「把端点解析成 openai-video」

最初尝试过这个方向,验证后确认不可行,两条原因:

  1. ChannelType2APIType(107) 返回 (APITypeOpenAI, false) —— 这些类型根本没注册relay.GetAdaptor() 仍然返回通用 OpenAI chat 适配器。
  2. 视频生成实际走 relay.GetTaskAdaptor()(如 taskbyteplus.TaskAdaptor),这是另一套注册表,同步测试链路 testChannel 完全够不着。

而且 channel-test.gorelayFormat 分支和 buildTestRequestWithOptions 都没有 openai-video 的 case。只改端点解析,结果仍是「chat 结构的 body + chat 适配器」,只是把 401 换成另一种错误。

方案

跳过这些渠道,与既有的 6 个异步任务渠道(Midjourney / Suno / Kling / Jimeng / Vidu / DoubaoVideo)处理方式一致 —— 新视频渠道当初只是漏加了。

func isVideoOnlyChannelType(channelType int) bool {
	for _, endpointType := range common.GetEndpointTypesByChannelType(channelType, "") {
		switch endpointType {
		case constant.EndpointTypeOpenAIVideo, constant.EndpointTypeVideo, constant.EndpointTypeVideoToMusic:
			return true
		}
	}
	return false
}

两个设计点:

  • 从端点注册表推导,不再硬编码第二份列表 —— 以后新增视频渠道自动覆盖,不会重蹈覆辙。
  • 只返回 localErrnewAPIError 保持 nil —— 定时循环里 ShouldDisableChannel(result.newAPIError) 只看 newAPIError,nil 就进不了自动禁用路径。已用断言锁死该行为。

测试

TDD 三轮,每轮先确认测试因正确原因失败。第二轮 RED 的 panic 栈显示执行穿过 channel-test.go:112 的守卫一路走到 :206GetUserCache,正是在准备发那个 chat 请求。

新增 3 个测试 / 17 个子用例,全部通过:

  • TestIsVideoOnlyChannelTypeCoversAsyncVideoChannels — 8 种视频渠道全部识别
  • TestIsVideoOnlyChannelTypeExcludesChatChannels — OpenAI/Anthropic/Gemini/Codex/VertexAI/Jina 不受影响
  • TestTestChannelSkipsVideoOnlyChannelsInsteadOfSendingChatRequest — 断言跳过且不产生 newAPIError

go vet ./controller/ 干净,Go 包全部编译通过。

controller 全量套件有 8 个失败(CLI device auth / default token / ProcessChannelError,均需 DB),已在 clean main 上 stash 验证为完全相同的 8 个,与本改动无关。

影响范围

不改变任何 chat 类渠道的测试行为。视频渠道从「被错误测试并禁用」变为「跳过测试」,与现有异步渠道行为一致。

备注

本 PR 只消除误禁用。渠道 #120 当前的 401 是其 API Key 本身格式非法(真实用户请求走正确视频端点也同样 401),需单独修 key 才能恢复 seedance 服务。

Scheduled channel testing sent POST /v1/chat/completions to channels that
only serve async video generation (BytePlus, TechMobi, Sora, Grok, MiniMax
H3, Sonilo, BlockRun). Those upstreams have no chat surface, so every probe
came back as an error, ShouldDisableChannel treated it as a channel fault,
and the auto-ban path disabled a healthy channel every test interval.

The endpoint registry already declared these channels as video-only via
GetEndpointTypesByChannelType, but testChannelWithOptions never consulted
it: normalizeChannelTestEndpoint returned "" for them, so requestPath fell
through to the chat default. Resolving the endpoint alone is not enough,
since video generation runs through relay.GetTaskAdaptor while the test
harness uses the synchronous relay.GetAdaptor registry, and
ChannelType2APIType does not register these types (it falls back to the
OpenAI chat adaptor).

Skip them instead, matching how the other async task channels (Midjourney,
Suno, Kling, Jimeng, Vidu, DoubaoVideo) are already handled. The guard
returns only localErr, leaving newAPIError nil so the scheduled loop cannot
route it into ShouldDisableChannel.

Derive the list from the endpoint registry rather than a second hardcoded
list, so newly added video channels are covered automatically.
@KingCesc

Copy link
Copy Markdown

🤖 OpenCodeReview · 评审 commit 82fb100e · 共 1 条

controller/channel-test.go

  • L63-64: [严重] 这里仅依赖 GetEndpointTypesByChannelType(channelType, "") 判断纯视频渠道,但已有异步视频任务渠道 ChannelTypeKuaiziLizhen 未在该映射中声明视频端点,会走 default 返回 OpenAI,因此仍会被 /v1/chat/completions 定时探测并可能误禁用健康的视频渠道。建议把纯视频任务渠道作为单一来源补齐到 endpoint 映射,或在这里显式兜底这些 task-only 渠道。
func isVideoOnlyChannelType(channelType int) bool {
	switch channelType {
	case constant.ChannelTypeKuaiziLizhen:
		return true
	}
	for _, endpointType := range common.GetEndpointTypesByChannelType(channelType, "") {

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.

2 participants