mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-15 23:22:59 +08:00
fix(openai-compat): Responses→Chat 桥接按 reasoning item id 缓存回注 reasoning_content
修复 #5520:Codex 经 force_chat_completions 桥接到 DeepSeek thinking 上游时, 历史中的 encrypted-only reasoning item(summary 为空 + 不透明 encrypted_content, 远程 compaction / 跨会话恢复后常见)取不出明文,后续 assistant 消息缺 reasoning_content,DeepSeek 400 "The reasoning_content in the thinking mode must be passed back to the API",客户端仅看到通用 502。 reasoning item 的 id 一定会被客户端回传,以其为 key 做服务端缓存: - GatewayCache 新增 Set/GetReasoningContent(Redis,默认 TTL 7 天) - 响应侧:流式扫 response.output_item.done、非流式扫 output,把 reasoning 全文按 item id 写缓存;客户端断连后 drain 期间用 detached ctx 仍会写完 - 请求侧:apicompat 新增 ResponsesToChatCompletionsRequestWithOptions 与 ReasoningContentByID 钩子,encrypted-only item 查缓存补回 pendingReasoning; 缓存 miss/出错一律 fail-open 维持原行为 - 自愈:历史里带明文 summary 的 reasoning item 顺手刷新缓存,覆盖 Redis flush / 跨实例漂移 测试:apicompat(命中恢复/miss 保持原样/明文优先)、service 端到端(流式 写缓存、请求侧回注+自愈)、repository miniredis 存取。
This commit is contained in:
@@ -131,6 +131,48 @@ func (c *gatewayCache) ReleaseGrokVideoBilled(ctx context.Context, key string) e
|
||||
var _ service.CyberSessionBlockStore = (*gatewayCache)(nil)
|
||||
var _ service.LiveCallStore = (*gatewayCache)(nil)
|
||||
|
||||
const reasoningContentPrefix = "reasoning_content:"
|
||||
|
||||
// reasoningContentDefaultTTL 是 reasoning 缓存的默认过期时间。Codex 会话可能
|
||||
// 跨多天恢复,取 7 天;调用方传入非正 TTL 时兜底。
|
||||
const reasoningContentDefaultTTL = 7 * 24 * time.Hour
|
||||
|
||||
// SetReasoningContent 按 reasoning item id 缓存 reasoning 全文。
|
||||
// itemID 或 content 为空时直接返回 nil(无可缓存内容,属正常情况而非错误)。
|
||||
func (c *gatewayCache) SetReasoningContent(ctx context.Context, itemID string, content string, ttl time.Duration) error {
|
||||
if c == nil || c.rdb == nil {
|
||||
return errors.New("gateway cache unavailable")
|
||||
}
|
||||
itemID = strings.TrimSpace(itemID)
|
||||
if itemID == "" || content == "" {
|
||||
return nil
|
||||
}
|
||||
if ttl <= 0 {
|
||||
ttl = reasoningContentDefaultTTL
|
||||
}
|
||||
return c.rdb.Set(ctx, reasoningContentPrefix+itemID, content, ttl).Err()
|
||||
}
|
||||
|
||||
// GetReasoningContent 返回缓存的 reasoning 全文;未命中返回
|
||||
// service.ErrReasoningContentNotFound。
|
||||
func (c *gatewayCache) GetReasoningContent(ctx context.Context, itemID string) (string, error) {
|
||||
if c == nil || c.rdb == nil {
|
||||
return "", errors.New("gateway cache unavailable")
|
||||
}
|
||||
itemID = strings.TrimSpace(itemID)
|
||||
if itemID == "" {
|
||||
return "", service.ErrReasoningContentNotFound
|
||||
}
|
||||
val, err := c.rdb.Get(ctx, reasoningContentPrefix+itemID).Result()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return "", service.ErrReasoningContentNotFound
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
const cyberSessionBlockPrefix = "cyber_session_block:"
|
||||
|
||||
// SetCyberSessionBlocked 把被 cyber_policy 命中的会话写入屏蔽表(TTL 自动过期)。
|
||||
|
||||
Reference in New Issue
Block a user