feat(responses): route /v1/responses through non-OpenAI adaptors via chat completions conversion - #2970
Closed
0-don wants to merge 5406 commits into
Closed
feat(responses): route /v1/responses through non-OpenAI adaptors via chat completions conversion#29700-don wants to merge 5406 commits into
0-don wants to merge 5406 commits into
Conversation
fix: /v1/chat/completions -> /v1/responses json_schema
将散落在多个文件中的预扣费/结算/退款逻辑抽象为统一的 BillingSession 生命周期管理: - 新增 BillingSettler 接口 (relay/common/billing.go) 避免循环引用 - 新增 FundingSource 接口 + WalletFunding / SubscriptionFunding 实现 (service/funding_source.go) - 新增 BillingSession 封装预扣/结算/退款原子操作 (service/billing_session.go) - 新增 SettleBilling 统一结算辅助函数,替换各 handler 中的 quotaDelta 模式 - 重写 PreConsumeBilling 为 BillingSession 工厂入口 - controller/relay.go 退款守卫改用 BillingSession.Refund() 修复的 Bug: - 令牌额度泄漏:PreConsumeTokenQuota 成功但 DecreaseUserQuota 失败时未回滚 - 订阅退款遗漏:FinalPreConsumedQuota=0 但 SubscriptionPreConsumed>0 时跳过退款 - 订阅多扣费:subConsume 强制为 1 但 FinalPreConsumedQuota 不同步 - 退款路径不统一:钱包/订阅退款逻辑现统一由 FundingSource.Refund 分派
- Settle 部分失败保护:新增 fundingSettled 标记,资金来源提交后 令牌调整失败不再导致 Refund 误退已结算的资金 - 订阅多扣费修复:trySubscription 传 subConsume 而非 preConsumedQuota 给 preConsume,保证三者(amount/preConsume/FinalPreConsumedQuota)一致 - 令牌回滚错误记录:preConsume 中 funding 失败时令牌回滚错误不再丢弃 - 移除钱包路径死代码:用户额度不足的 strings.Contains 匹配不可能命中 - WalletFunding.Refund 不重试:IncreaseUserQuota 非幂等,重试会多退
…e recharge card tabs - Defaulting to subscriptions when available and avoiding initial flash when no plans exist. - Adjust the wide-screen layout to place wallet and invite sections side by side, simplify the subscription header and controls, and add padding to prevent card borders from clipping. - Update related i18n strings by adding the new tab label and removing the obsolete subscription blurb.
…-when-no-plans ✨ refactor(wallet): Top-up layout to embed subscription plans into the recharge card tabs
refactor: 抽象统一计费会话 BillingSession
Add a lightweight active-subscription check to skip subscription pre-consume when none exist, reducing unnecessary transactions and locks. In the subscription UI, disable subscription-first options when no active plan is available, show the effective fallback to wallet with a clear notice, and distinguish “invalidated” from “expired” states. Update i18n strings across supported locales to reflect the new messages and status labels.
Aligns the error variable types in the subscription-first path so that quota fallback checks use the correct NewAPIError. This prevents build failures and preserves the intended wallet fallback when subscription pre-consume returns an insufficient quota error.
Routes quota alerts through a subscription-specific check when billing from subscriptions, preventing wallet-based thresholds from triggering false warnings. Updates the notification settings description and localization keys to clarify that both wallet and subscription balances are monitored.
🔔 feat: Add subscription-aware quota notifications and update UI copy
…-fallback ✨ chore: Improve subscription billing fallback and UI states
当上游为 AWS Bedrock 时,message_delta 的 usage 可能缺少 input_tokens、 cache_creation_input_tokens、cache_read_input_tokens 等字段,导致与原生 Anthropic 格式不一致。从 message_start 积累的 claudeInfo 中补全这些字段后 重新序列化,确保客户端收到一致的 usage 格式。
Modified the formatUserLogs function to include a startIdx parameter, allowing for more flexible log ID assignment. Updated calls to this function in GetLogByTokenId and GetUserLogs to pass the appropriate starting index.
feat: add Codex channel disclaimer (i18n, OpenAI terms)
feat: Force beta=true parameter for Anthropic channel
feat(oauth): implement custom OAuth provider
fix: Claude stream block index/type transitions
fix: add paragraph breaks between reasoning summary chunks
# Conflicts: # service/openaicompat/chat_to_responses.go
feat: channel test with stream=true
fix: 使用openai兼容接口调用部分渠道在最终端点为claude原生端点下还是走了openai扣减input_token的逻辑
fix: 补全 streaming message_delta 事件缺失的 input_tokens 和 cache 相关字段
feat: /v1/messages -> /v1/responses
…rable feat: make 5m cache-creation ratio configurable
…eader-append feat:support $keep_only_declared and deduped $append for header override
0-don
force-pushed
the
feat/responses-via-chat-completions
branch
from
March 10, 2026 18:47
1875596 to
00ae74b
Compare
chore: update model lists for frequently used channels
0-don
force-pushed
the
feat/responses-via-chat-completions
branch
3 times, most recently
from
March 13, 2026 12:32
59a03f9 to
ae1b48a
Compare
…tion links and submission checks
0-don
force-pushed
the
feat/responses-via-chat-completions
branch
from
March 14, 2026 15:59
ae1b48a to
34d9d40
Compare
Round remaining balance
enhance channel key viewing
…cff3f3572f535919 feat: add logs content tooltip
…chat completions conversion Enable channels without native Responses API support (starting with Claude) to serve /v1/responses by converting Responses to Chat Completions format, routing through the existing adaptor pipeline, and converting responses back. Converters (adaptor-agnostic, reusable for any future adaptor): - ResponsesRequestToChatCompletionsRequest: handles developer role mapping, function_call/function_call_output merging, tool passthrough, response format conversion, PromptCacheKey/PromptCacheRetention - ChatCompletionsResponseToResponsesResponse: non-streaming conversion - ChatToResponsesStreamState: streaming state machine that emits proper Responses SSE events (response.created, output_text.delta, function_call_arguments.delta, response.completed, usage) Claude adaptor: 3-line integration chaining ResponsesRequest to ChatCompletions to native Claude format via existing conversion path. Bug fix from #2892: tool calls no longer dropped when text content exists in ResponsesResponseToChatCompletionsResponse. Related: #2941, #2043, #1527
0-don
force-pushed
the
feat/responses-via-chat-completions
branch
from
March 16, 2026 23:29
34d9d40 to
55cf511
Compare
Contributor
Author
|
Addressed remaining CodeRabbit feedback:
|
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
Enable channels without native Responses API support (starting with Claude) to serve
/v1/responsesrequests by converting Responses to Chat Completions format, routing through the existing adaptor pipeline, and converting responses back. Any future adaptor (Gemini, Deepseek, etc.) gets support by adding the same 3-line pattern.Related: #2941, #2043, #1527
Why not use existing PRs directly?
vs #2892 (494 lines, Claude-only)
RequestOpenAI2ClaudeMessage(max tokens, thinking, tools, instructions, tool choice)vs #2817 (1039 lines)
SummaryIndexfieldConvertOpenAIResponsesRequestpattern,GetJsonType()for input detection,developertosystemrole mapping,PromptCacheKey/PromptCacheRetentionpassthrough, non-function tool passthrough viaCustomfieldArchitecture (1006 lines, adaptor-agnostic)
Converters (reusable for any adaptor):
ResponsesRequestToChatCompletionsRequest: handles developer role mapping, function_call/function_call_output merging into tool messages, content type detection viaGetJsonType(), response format conversion, PromptCacheKey/PromptCacheRetention passthroughChatCompletionsResponseToResponsesResponse: non-streaming response conversionChatToResponsesStreamState: streaming state machine emitting proper Responses SSE events (response.created,response.output_text.delta,response.function_call_arguments.delta,response.completed, usage)Claude adaptor: 3 lines chaining
ResponsesRequestToChatCompletionsRequestthenConvertOpenAIRequest(reuses all existing Claude conversion logic)Files changed
relay/channel/claude/adaptor.goConvertOpenAIResponsesRequest(+5 lines)relay/channel/claude/relay-claude.goRelayFormatOpenAIResponsesto stream/non-stream handlersservice/openaicompat/responses_to_chat.goservice/openaicompat/chat_stream_to_responses_stream.goservice/openai_chat_responses_compat.godto/openai_response.goResponsesStreamResponseTest plan
/v1/responsesthrough Claude channel (verify SSE events match Responses API format)/v1/responsesthrough Claude channel