Skip to content

feat(channel): OpenAI 兼容渠道支持双向推理字段改名开关 - #626

Open
lianginx wants to merge 1 commit into
tbphp:mainfrom
lianginx:feat/reasoning-alias
Open

feat(channel): OpenAI 兼容渠道支持双向推理字段改名开关#626
lianginx wants to merge 1 commit into
tbphp:mainfrom
lianginx:feat/reasoning-alias

Conversation

@lianginx

@lianginx lianginx commented Sep 11, 2026

Copy link
Copy Markdown

关联 Issue / Related Issue

Closes #627

变更内容 / Change Content

  • 新功能 / New feature

不同上游对"思考内容"字段的拼写不一致:vLLM 等只返回 reasoning,SGLang 等只返回 reasoning_content;而客户端/调用方往往只识别其中一种拼写,导致思考内容在链路中被静默丢弃。背景与调查详见 #627

本 PR 为 OpenAI Compatible 渠道新增两个推理字段开关,在网关侧统一上下游的拼写:

  • 响应方向reasoning_content_alias):off / duplicate 两态。开启后在 choices[].message/delta 中同时输出 reasoningreasoning_content(值相同),各类客户端各取所需。
  • 请求方向request_reasoning_alias):off / reasoning → reasoning_content / reasoning_content → reasoning 三态,改写出站历史消息中的推理字段拼写,供只识别另一种拼写的上游回放思考内容。

实现要点:

  • 双写只填充缺失、null 或空字符串的目标拼写,两个非空拼写并存时保持原样;请求方向改名采用移动语义:源字段值写入目标字段(目标已有非空值时保留目标值),源字段始终移除,与开关字面意义一致。
  • 参数为渠道 spec 声明的 select 字段(reasoning_content_alias / request_reasoning_alias),默认关闭;关闭时零开销——按字段标记做快速路径,未命中直接原样穿透。
  • 仅对 OpenAI Chat Completions 原生协议生效;带 Content-Encoding 或非法 JSON 的响应不做改动,保证字节级安全。
  • WebUI:分组设置页新增「推理字段」独立章节(响应方向两态、请求方向三态,中/英/日三语);导入向导不渲染 select 类参数。

性能(Apple M4,go test -bench):

场景 耗时
响应改写 1KB / 58KB 5.6µs / 114µs
请求改写 11KB / 177KB / 2.8MB 77µs / 1.1ms / 17ms
请求/响应里根本没有推理字段 原样透传,唯一开销是先通读一遍确认字段不存在(约 4GB/s,10MB 约 2.5ms)

兼容性说明

  • 无数据库迁移;参数可选、默认关闭,关闭时行为与现状完全一致,存量分组不受影响。

WebUI 截图

image

自查清单 / Checklist

  • 我已运行 make check,或在说明中写明无法运行的原因和未验证范围。
  • 本 PR 范围聚焦,未包含无关改动。
  • 我已更新必要的公开文档或发布说明(三份 README 各新增一条能力说明;官方文档站可在合并后另行跟进)。
  • 我已确认提交、日志和测试数据不包含敏感信息。
  • 如适用,我已说明兼容性或数据迁移影响。

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

本次变更为 OpenAI 兼容渠道增加 reasoningreasoning_content 的双向改名配置。后端新增 select 字段定义、参数规范化、请求消息改写、JSON 响应改写和 SSE 事件改写。Bifrost 在 OpenAI Completions 协议路径中接入这些改写。WebUI 新增推理字段设置分区、下拉选项、路由支持和多语言文案。测试覆盖配置、请求、响应及 SSE 行为。

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🟡 Moderate · up to fe5eb

Existing configurations can fail validation after upgrade, and enabled request or response rewriting can alter malformed payloads by dropping trailing data. These should be corrected before merge; the remaining settings and documentation issues can also mislead users.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR 已实现 #627 的大部分编码目标:响应方向 off/duplicate、请求方向两种改名、OpenAI Chat Completions 直通请求、非流式 JSON、SSE、移动语义、压缩响应和非法 JSON保护,以及 WebUI 和测试。当前 head 仍将请求改名限制在 protocol.OpenAICompletions 直通路径;prepare 的类型化路径没有等价的请… 需要为非直通路径提供可配置的出站消息字段改写,或让 Bifrost 的类型化序列化支持请求别名,并增加对应集成测试。需要恢复历史布尔配置的读取和迁移逻辑,并增加旧布尔值到新规范值的测试。
Docstring Coverage ⚠️ Warning Docstring coverage is 40.74% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 54 functions across 21 files. (4 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed 变更均与 #627 直接相关。InputSelect、参数规范化、请求和响应改写、协议判断、WebUI 路由、本地化文案、README 和测试共同实现推理字段兼容配置。未发现与该功能无关的变更。
Description check ✅ Passed PR 描述完整遵循模板,包含关联 Issue、变更内容、详细实现说明、兼容性影响和完整自查清单。
Title check ✅ Passed 标题明确说明了 OpenAI 兼容渠道新增双向推理字段改名开关,准确概括了 PR 的主要变更。
Full details: Linked Issues check

Explanation

PR 已实现 #627 的大部分编码目标:响应方向 off/duplicate、请求方向两种改名、OpenAI Chat Completions 直通请求、非流式 JSON、SSE、移动语义、压缩响应和非法 JSON保护,以及 WebUI 和测试。当前 head 仍将请求改名限制在 protocol.OpenAICompletions 直通路径;prepare 的类型化路径没有等价的请求改写钩子。因此,#627 原要求的 OpenAI 兼容渠道出站历史消息全覆盖仍不满足,尤其是非直通模式的组合。另有配置兼容性问题:normalizeReasoningAliasOption 不再接受历史布尔值,reasoningAliasText 也只解析字符串,现有测试还明确拒绝 true/false,与 PR 目标中的历史布尔值兼容和自动迁移不一致。

Full details: Docstring Coverage

Explanation

Docstring coverage is 40.74% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 54 functions across 21 files. (4 skipped: 4 unsupported.)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
internal/execution/bifrost/executor.go-629-631 (1)

629-631: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

在 WebUI 中说明 request_reasoning_alias 的适用范围。 ProviderOpenAICompatible 只有在 base_url/v1 结尾时才进入包含 RewriteRequestMessages 的 passthrough 分支。其他配置会进入 typed 路径,该路径将两种字段统一到内部 Reasoning,再固定输出为 reasoning_content。因此,reasoning_content → reasoning 在该路径上不会生效;reasoning → reasoning_content 可能由 typed 转换自然实现。请在 WebUI 中说明该方向和 base_url 限制,或让 typed 路径执行相同的请求改名逻辑。

web/src/features/groups/settings/GroupSettingsTab.vue-630-630 (1)

630-630: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

在不支持推理字段时将深链接重定向到 general

selectedChannel 已解析且没有 reasoning_content_aliasrequest_reasoning_alias 时,GroupSettingsTab.vue 会同时移除 settings-reasoning 导航项和对应的 GroupSettingsBaseFormgroup-route.ts 仍会接受 section=reasoning,因此该 URL 没有匹配内容;GroupSettingsBaseForm 中的 group.settings.reasoning.notSupported 也无法渲染。请在确认渠道不支持后使用 router.replacesection 改为 general

🧹 Nitpick comments (1)
internal/execution/bifrost/model_alias.go (1)

152-181: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

缓存请求和响应的 alias mode

当前 OpenAI passthrough 请求在 alias 全部关闭时执行 2 次 TargetConfigjson.Unmarshal。在请求和响应 alias 都启用时执行 5 次,其中请求路径执行 3 次,响应路径执行 2 次。因此“功能关闭时每个请求多出 3 次解析”不准确,但启用 alias 时确实存在重复的完整配置解析。

请让 responseReasoningAliasModerequestReasoningAliasMode 各自只解析一次,并让 needsResponseReasoningAliasneedsRequestReasoningAlias 复用对应的 mode。executor.go 应缓存一次 requestReasoningAliasMode 的结果,再将该结果传给 RewriteRequestMessages,避免先调用 needsRequestReasoningAlias、再重复计算 mode。


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: 348b37de-2139-444e-8b6e-abb5118d8928

📥 Commits

Reviewing files that changed from the base of the PR and between 33ec668 and ba69506.

📒 Files selected for processing (27)
  • README.md
  • README_CN.md
  • README_JP.md
  • internal/channel/channel.go
  • internal/channel/compiler.go
  • internal/channel/modules/openai_compatible.go
  • internal/channel/reasoning_alias_params_test.go
  • internal/channel/registry_test.go
  • internal/channel/spec/definition.go
  • internal/channel/spec/normalize.go
  • internal/execution/bifrost/executor.go
  • internal/execution/bifrost/model_alias.go
  • internal/execution/bifrost/passthrough.go
  • internal/execution/bifrost/reasoning_alias_modes_test.go
  • internal/execution/responsealias/reasoning_alias.go
  • internal/execution/responsealias/reasoning_alias_test.go
  • internal/execution/responsealias/request_alias.go
  • internal/execution/responsealias/request_alias_test.go
  • internal/execution/responsealias/response_alias.go
  • web/src/app/resources/channels.ts
  • web/src/features/groups/group-route.ts
  • web/src/features/groups/settings/GroupSettingsBaseForm.vue
  • web/src/features/groups/settings/GroupSettingsTab.vue
  • web/src/features/import/ImportConnectionSection.vue
  • web/src/i18n/locales/en-US/group.ts
  • web/src/i18n/locales/ja-JP/group.ts
  • web/src/i18n/locales/zh-CN/group.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

@lianginx

Copy link
Copy Markdown
Author

ProviderOpenAICompatible 只有在 base_url 以 /v1 结尾时才进入包含 RewriteRequestMessages 的 passthrough 分支。其他配置会进入 typed 路径……因此,reasoning_content → reasoning 在该路径上不会生效。

该问题属实,覆盖范围说明如下。

请求方向的重命名目前仅在直通模式下生效。原因是非直通模式的请求体由外部依赖 Bifrost 组装并发送,gpt-load 无法介入其序列化过程。

Bifrost 自身的处理方式覆盖了其中大部分场景:它解析客户端回传的历史消息时同时接受 reasoningreasoning_content,发往上游时固定使用 reasoning_content。结合实测的上游行为:

  • vLLM:回传历史时两种拼写均接受 → 不受影响
  • SGLang:仅接受 reasoning_content → 与 Bifrost 的固定拼写一致,不受影响

未覆盖的仅剩一种组合:上游仅接受 reasoning,且渠道走非直通模式(OpenAI 兼容渠道 base_url 不以 /v1 结尾时触发)。目前尚无实际案例。

响应方向不存在此问题:两种模式下客户端均可正常获取推理内容,非直通模式下 Bifrost 会在响应中同时输出两种拼写。

如需完全覆盖该组合,需要使 Bifrost 出站 wire 类型的拼写可配置,属于上游依赖改动,不在本 PR 范围内。

@lianginx
lianginx force-pushed the feat/reasoning-alias branch 2 times, most recently from e1aa6ab to 6beb6a0 Compare September 11, 2026 08:29
@lianginx

Copy link
Copy Markdown
Author

请让 typed 路径执行相同的请求改名逻辑,或在 WebUI 中说明该方向和 base_url 限制。

已按建议在 WebUI 中补充适用范围说明(请求方向 help 文案),见 commit 6beb6a0
「重命名出站消息中的推理字段(仅 base_url 以 /v1 结尾时生效)。」

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/features/groups/settings/GroupSettingsTab.vue (1)

630-630: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

当动态导航不包含请求的 section 时回退

当 channel 没有 reasoning_content_aliasrequest_reasoning_alias 时,navItems 不包含 settings-reasoning,且 reasoning form 不渲染。路由仍接受 section=reasoningGroupSettingsTab 将该值传给 useSectionNavigation,而 selectSection 会保留不存在的 ID,并跳过对缺失 DOM 元素的滚动。因此深链接无法滚动到可用的设置 section,桌面导航也没有 active 项。请在 GroupSettingsTab 的动态导航边界统一归一化请求值,并在 initialId 和路由 watcher 中复用该结果;当请求值不在 navItems 中时,使用 navItems[0].id

🟡 Other comments (1)
web/src/i18n/locales/en-US/group.ts-221-222 (1)

221-222: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

统一三种语言中请求方向的作用范围说明。

三种语言的 reasoning 区域文案都把请求方向概括为出站请求,但本 PR 只在直通模式改写出站历史消息。请在以下位置加入“直通模式”和“历史消息”的限制:

  • web/src/i18n/locales/en-US/group.ts#L221-L222: 明确 request alias 只改写直通模式发送的历史消息。
  • web/src/i18n/locales/ja-JP/group.ts#L221-L222: 使用对应日文文案明确直通模式和历史消息范围。
  • web/src/i18n/locales/zh-CN/group.ts#L218-L219: 使用对应中文文案明确直通模式和历史消息范围。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: e8eae1da-6e4c-481c-8abf-3057979f8605

📥 Commits

Reviewing files that changed from the base of the PR and between ba69506 and 6beb6a0.

📒 Files selected for processing (3)
  • web/src/i18n/locales/en-US/group.ts
  • web/src/i18n/locales/ja-JP/group.ts
  • web/src/i18n/locales/zh-CN/group.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.

@lianginx

lianginx commented Sep 11, 2026

Copy link
Copy Markdown
Author

提交后我们复盘了方案的覆盖面,发现响应方向的设计有一个问题,计划在本 PR 中修正,先在此说明。

问题:响应方向不应该强迫管理员二选一。 当前的响应开关要求管理员在 reasoning_content -> reasoningreasoning_content -> reasoning 之间选一个,前提是他确切知道组内所有客户端解析哪种拼写。但客户端对网关是黑盒:同一分组里可能同时存在只解析 reasoning_content 和只解析 reasoning 的客户端,且管理员无法预知未来接入的客户端。无论往哪边改名,都服务不了这种共存场景。

对策:响应方向改为"关闭 / 双写"两态。 开启后响应中同时输出 reasoningreasoning_content(值相同),各类客户端各取所需,网关不需要猜测任何客户端的方言。这个行为有现成先例:Bifrost 非直通路径的响应类型 ChatAssistantMessage / ChatStreamResponseChoiceDelta 的 MarshalJSON 原生双拼写输出,其注释记载的动机与我们完全相同——只发一种拼写时,另一种客户端"眼睁睁看着思考内容在场却读不到"。

请求方向则还存在一个未解决的问题。 客户端这一侧双写可以覆盖所有情况,上游这一侧更复杂一些:如果上游各模型提供统一的输入格式,端点级改名够用;但如果上游本身是另一个中转站,很可能每个模型需要的推理字段都不一样——模型 A 只接受 reasoning_content、模型 B 只接受 reasoning、或者两者都支持——只在分组级别配置转换是不够用的。或许可以把上游推理字段的配置细化到模型级别,这点我还在思考。目前分组级别的配置对我们自己的部署已经够用,但对一个通用性的 PR 来说可能还不够。

- 响应方向 reasoning_content_alias 两态:off / duplicate,开启后在 choices[].message/delta 同时输出 reasoning 与 reasoning_content(仅填充缺失、null 或空串目标),非流式 JSON 与流式 SSE 均生效
- 请求方向 request_reasoning_alias 三态:off / reasoning_to_content / reasoning_content_to_reasoning,按移动语义改写出站历史消息
- 参数校验只接受规范值,关闭时零开销
- WebUI「推理字段」章节(响应方向两态、请求方向三态,中/英/日三语),三份 README 同步
@lianginx
lianginx force-pushed the feat/reasoning-alias branch from 6beb6a0 to fe5eb0d Compare September 12, 2026 03:28
@lianginx

Copy link
Copy Markdown
Author

按上一条评论的方案完成调整并推送。PR 已整理为单一提交,描述同步更新为最终设计:

  • 响应方向改为两态reasoning_content_alias 只接受 off / duplicate。开启后响应中同时输出 reasoningreasoning_content(仅填充缺失、null 或空串的目标拼写,两个非空拼写并存时不动),JSON 与 SSE 均生效,与 Bifrost 非直通路径的原生双拼写输出一致。
  • 请求方向维持三态改名request_reasoning_aliasoff / reasoning → reasoning_content / reasoning_content → reasoning

go build、全量 go test、前端 type-check 均通过。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
web/src/i18n/locales/en-US/group.ts-220-223 (1)

220-223: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

/v1 限制仅说明为请求方向的限制。

当前文案将该限制同时应用到响应方向。响应别名处理仍适用于 OpenAI Chat Completions 的 JSON 和 SSE 响应。用户会因此误以为非直通渠道不能使用响应双写,并无法配置此功能。

  • web/src/i18n/locales/en-US/group.ts#L220-L223:将 “only takes effect when base_url ends with /v1” 限定为请求方向。
  • web/src/i18n/locales/ja-JP/group.ts#L220-L223:将 /v1 条件限定为请求方向。
  • web/src/i18n/locales/zh-CN/group.ts#L217-L220:明确只有请求方向仅在 base_url/v1 结尾时生效。
README.md-61-61 (1)

61-61: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

统一修正三个 README 的推理字段兼容说明。

  • reasoning_content_alias=duplicate 才会启用响应双写。
  • request_reasoning_alias 设为 reasoning_to_contentreasoning_content_to_reasoning 才会启用请求改名。
  • 请求改名只处理聊天请求顶层 messages 数组中的消息对象,不处理其他出站字段。
  • 两项能力仅适用于 OpenAI Chat Completions;请求改名还仅在原生 /v1 直通路径生效。

请在 README.mdREADME_CN.mdREADME_JP.md 的对应条目中补充这些配置和范围限制。


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: 3abb8d2c-ac1b-4052-8207-a36ab5288829

📥 Commits

Reviewing files that changed from the base of the PR and between 6beb6a0 and fe5eb0d.

📒 Files selected for processing (20)
  • README.md
  • README_CN.md
  • README_JP.md
  • internal/channel/modules/openai_compatible.go
  • internal/channel/reasoning_alias_params_test.go
  • internal/channel/registry_test.go
  • internal/channel/spec/definition.go
  • internal/channel/spec/normalize.go
  • internal/execution/bifrost/executor.go
  • internal/execution/bifrost/model_alias.go
  • internal/execution/bifrost/reasoning_alias_modes_test.go
  • internal/execution/responsealias/reasoning_alias.go
  • internal/execution/responsealias/reasoning_alias_test.go
  • internal/execution/responsealias/request_alias.go
  • internal/execution/responsealias/request_alias_test.go
  • internal/execution/responsealias/response_alias.go
  • web/src/features/groups/settings/GroupSettingsBaseForm.vue
  • web/src/i18n/locales/en-US/group.ts
  • web/src/i18n/locales/ja-JP/group.ts
  • web/src/i18n/locales/zh-CN/group.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment on lines +77 to +87
func normalizeReasoningAliasOption(value string, options []string) (string, error) {
normalized := strings.ToLower(strings.TrimSpace(value))
if normalized == "" {
return "", nil
}
for _, option := range options {
if normalized == option {
return option, nil
}
}
return "", fmt.Errorf("must be one of %s", strings.Join(options, ", "))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 检查历史 reasoning_content_alias 布尔值是否在参数验证前转换。
rg -n -C 5 \
  'reasoning_content_alias|NormalizeResponseReasoningAlias|ValidateParams|TargetConfig|ReasoningAliasDuplicate' \
  internal/channel/compiler.go internal/channel/channel.go internal/execution/bifrost/model_alias.go

# 检查是否已有旧布尔值配置的迁移或读取回归测试。
rg -n -C 4 \
  '"reasoning_content_alias":(true|false)|reasoning_content_alias.*true|reasoning_content_alias.*false' \
  internal --glob '*_test.go'

Repository: tbphp/gpt-load

Length of output: 14890


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- ValidateParams and parameter decoding ---'
sed -n '470,545p' internal/channel/channel.go
printf '%s\n' '--- parameter schema and normalization symbols ---'
rg -n -C 4 'type Params|func \(.*\) Value|CanonicalJSON|normalizeReasoningAliasOption|reasoning_content_alias|ResolveExecutionTarget|\\.Resolve\\(' internal/channel internal/execution --glob '*.go'
printf '%s\n' '--- persisted group-parameter read paths ---'
rg -n -C 5 'TargetConfig|target_config|group.*param|params.*group|ResolveExecutionTarget|Resolve\\(' internal --glob '*.go' --glob '*_test.go'
printf '%s\n' '--- relevant tests ---'
sed -n '1,130p' internal/channel/reasoning_alias_params_test.go

Repository: tbphp/gpt-load

Length of output: 2918


🤖 get_repo_knowledge executed:

get_repo_knowledge tbphp/gpt-load /tmp/coderabbit-repo-knowledge/tbphp-gpt-load-62eb2efe/learnings

Length of output: 727


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- parameter types and validators ---'
rg -n -C 5 'type Params|func .*Value|CanonicalJSON|normalizeReasoningAliasOption|reasoning_content_alias' internal/channel internal/execution --glob '*.go'

printf '%s\n' '--- target resolution callers and persisted configuration paths ---'
rg -n -C 5 'ResolveExecutionTarget|Resolve\(|TargetConfig|target_config|group_params|GroupParams|group.*params|params.*group' internal --glob '*.go' --glob '*_test.go'

printf '%s\n' '--- channel tests around historical values ---'
sed -n '1,130p' internal/channel/reasoning_alias_params_test.go

Repository: tbphp/gpt-load

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- exact validator definitions ---'
rg -l 'normalizeReasoningAliasOption|type Params|func .*validate' internal/channel
for f in $(rg -l 'normalizeReasoningAliasOption|type Params|func .*validate' internal/channel); do
  printf '\n--- %s ---\n' "$f"
  rg -n -C 8 'normalizeReasoningAliasOption|type Params|func .*validate|reasoning_content_alias|request_reasoning_alias' "$f"
done

printf '%s\n' '--- state loading and snapshot parameter flow ---'
rg -n -C 6 'ChannelRegistry.ValidateParams|group.Params|Params:|params:' internal/state/loader internal/state --glob '*.go' --glob '*_test.go' | head -n 320

printf '%s\n' '--- group model and loader conversion ---'
rg -n -C 8 'type Group|Params .*JSON|GroupConfig|group.*Params|Params.*group' internal/models internal/state/loader internal/state --glob '*.go' --glob '*_test.go' | head -n 360

Repository: tbphp/gpt-load

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- reasoning alias binding ---'
rg -l 'NormalizeResponseReasoningAlias|NormalizeReasoningAlias|reasoning_content_alias' internal/channel --glob '*.go'
rg -n -C 12 'NormalizeResponseReasoningAlias|NormalizeReasoningAlias|reasoning_content_alias|request_reasoning_alias' internal/channel/modules internal/channel --glob '*.go' | head -n 260

printf '%s\n' '--- strict object decoding and field normalization ---'
sed -n '600,690p' internal/channel/channel.go
rg -n -C 8 'func decodeStrictObject|type fieldSpec|Normalizer|normalize' internal/channel --glob '*.go'

printf '%s\n' '--- loader pass-through and compile validation ---'
sed -n '588,670p' internal/state/loader/loader.go
sed -n '248,275p' internal/state/snapshot.go

Repository: tbphp/gpt-load

Length of output: 50371


在加载持久化组参数时迁移历史布尔值。

如果启用的历史组参数包含 reasoning_content_alias: truefalseinternal/state/loader/loader.go 会将 row.Params 原样传入 GroupConfig.Params。随后 internal/state/snapshot.go 调用 Registry.ValidateParams,该验证器要求每个字段值为 JSON 字符串,因此会在执行 normalizeReasoningAliasOption 前返回 must be a string。请在验证前按既定映射转换历史布尔值,并添加加载路径的回归测试。

decoder := json.NewDecoder(bytes.NewReader(payload))
decoder.UseNumber()
var doc map[string]any
if err := decoder.Decode(&doc); err != nil || doc == nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🤖 get_repo_knowledge executed:

get_repo_knowledge tbphp/gpt-load /tmp/coderabbit-repo-knowledge/tbphp-gpt-load-62eb2efe

Length of output: 418


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- reasoning_alias.go ---'
cat -n internal/execution/responsealias/reasoning_alias.go
printf '%s\n' '--- request_alias.go ---'
cat -n internal/execution/responsealias/request_alias.go
printf '%s\n' '--- responsealias files ---'
git ls-files 'internal/execution/responsealias/*'
printf '%s\n' '--- relevant contracts and callers ---'
rg -n --glob '*.go' 'responsealias|ReasoningAlias|RequestAlias|Decode\(&doc\)|json\.NewDecoder' internal

Repository: tbphp/gpt-load

Length of output: 28936


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- responsealias tests: trailing and invalid-input behavior ---'
rg -n -C 5 'trailing|invalid|malformed|byte-identical|unchanged|junk|RewriteRequestMessages|normalizeOpenAIReasoning' \
  internal/execution/responsealias/*_test.go
printf '%s\n' '--- response_alias.go ---'
cat -n internal/execution/responsealias/response_alias.go
printf '%s\n' '--- call-site slices ---'
sed -n '600,645p' internal/execution/bifrost/executor.go
sed -n '180,215p' internal/execution/bifrost/model_alias.go
sed -n '1,90p' internal/execution/responsealias/request_alias_test.go
sed -n '1,100p' internal/execution/responsealias/reasoning_alias_test.go

Repository: tbphp/gpt-load

Length of output: 26871


🌐 Web query:

Go encoding/json Decoder.Decode documentation trailing JSON values

💡 Result:

In Go's encoding/json package, the Decoder.Decode method is designed to read only the next single JSON-encoded value from the input stream [1][2]. It does not consume or validate any data that may exist in the stream after that value [3][4]. If your input contains trailing JSON values or other data, the Decoder will silently ignore them [3]. Because Decode only processes the first complete JSON object it encounters, any subsequent data remains in the reader or the decoder's internal buffer [1][2]. To detect or handle trailing data, you can use the following approach: 1. After calling Decode, use the Decoder.Token method to check for additional input [3]. 2. If Token returns anything other than io.EOF (after skipping any whitespace), it indicates that there is extra, unparsed data in the stream [3]. Example implementation: decoder:= json.NewDecoder(r) if err:= decoder.Decode(&v); err!= nil { // Handle error } // Check for trailing data if _, err:= decoder.Token; err!= io.EOF { // Found trailing data in the stream } Note that Decoder.Buffered can be used to inspect any data that has already been read from the underlying io.Reader into the decoder's internal buffer but has not yet been processed by Decode [1][2]. However, Token is generally the preferred, more idiomatic way to verify the end of the expected input [3].

Citations:


确保输入只包含一个 JSON 值。

json.Decoder.Decode 只读取下一个 JSON 值。对于带有尾随数据的输入,第一次 Decode 仍可能成功。若消息或推理字段发生改写,json.Marshal 会丢弃尾随数据。

请在 internal/execution/responsealias/reasoning_alias.gointernal/execution/responsealias/request_alias.go 中,在第一次 Decode 后执行第二次 Decode。只有第二次 Decode 返回 io.EOF 时,才允许改写;否则返回原始字节。

📍 Affects 2 files
  • internal/execution/responsealias/reasoning_alias.go#L28-L28 (this comment)
  • internal/execution/responsealias/request_alias.go#L32-L32

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.

建议:OpenAI 兼容渠道支持推理字段双向改名(reasoning ↔ reasoning_content)

1 participant