mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
修复 #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 存取。
42 lines
1.4 KiB
Go
42 lines
1.4 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/redis/go-redis/v9"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGatewayCacheReasoningContent(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
client := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
cache := NewGatewayCache(client)
|
|
ctx := context.Background()
|
|
|
|
// 未命中返回哨兵错误,区别于真实读取失败。
|
|
_, err := cache.GetReasoningContent(ctx, "item_missing")
|
|
require.ErrorIs(t, err, service.ErrReasoningContentNotFound)
|
|
|
|
// 写入后可读回。
|
|
require.NoError(t, cache.SetReasoningContent(ctx, "item_abc", "think hard", time.Minute))
|
|
got, err := cache.GetReasoningContent(ctx, "item_abc")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "think hard", got)
|
|
|
|
// ttl<=0 时兜底为默认 7 天。
|
|
require.NoError(t, cache.SetReasoningContent(ctx, "item_ttl", "x", 0))
|
|
ttl := mr.TTL(reasoningContentPrefix + "item_ttl")
|
|
require.Greater(t, ttl, 6*24*time.Hour)
|
|
require.LessOrEqual(t, ttl, reasoningContentDefaultTTL)
|
|
|
|
// 空 itemID / 空 content 是 no-op(无可缓存内容不算错误)。
|
|
require.NoError(t, cache.SetReasoningContent(ctx, "", "x", time.Minute))
|
|
require.NoError(t, cache.SetReasoningContent(ctx, "item_empty", "", time.Minute))
|
|
_, err = cache.GetReasoningContent(ctx, "item_empty")
|
|
require.ErrorIs(t, err, service.ErrReasoningContentNotFound)
|
|
}
|