Preserve Codex reasoning signatures for Claude
This commit is contained in:
@@ -243,6 +243,147 @@ func TestConvertCodexResponseToClaude_StreamThinkingUsesEarlyCapturedSignatureWh
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertCodexResponseToClaude_StreamThinkingUsesFinalDoneSignature(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
originalRequest := []byte(`{"messages":[]}`)
|
||||
var param any
|
||||
|
||||
chunks := [][]byte{
|
||||
[]byte("data: {\"type\":\"response.output_item.added\",\"item\":{\"type\":\"reasoning\",\"encrypted_content\":\"enc_sig_initial\"}}"),
|
||||
[]byte("data: {\"type\":\"response.reasoning_summary_part.added\"}"),
|
||||
[]byte("data: {\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"Let me think\"}"),
|
||||
[]byte("data: {\"type\":\"response.reasoning_summary_part.done\"}"),
|
||||
[]byte("data: {\"type\":\"response.output_item.done\",\"item\":{\"type\":\"reasoning\",\"encrypted_content\":\"enc_sig_final\"}}"),
|
||||
}
|
||||
|
||||
var outputs [][]byte
|
||||
for _, chunk := range chunks {
|
||||
outputs = append(outputs, ConvertCodexResponseToClaude(ctx, "", originalRequest, nil, chunk, ¶m)...)
|
||||
}
|
||||
|
||||
signatureDeltaCount := 0
|
||||
events := []string{}
|
||||
for _, out := range outputs {
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if !strings.HasPrefix(line, "data: ") {
|
||||
continue
|
||||
}
|
||||
data := gjson.Parse(strings.TrimPrefix(line, "data: "))
|
||||
if data.Get("type").String() == "content_block_start" && data.Get("content_block.type").String() == "thinking" {
|
||||
events = append(events, "thinking_start")
|
||||
}
|
||||
if data.Get("type").String() == "content_block_delta" && data.Get("delta.type").String() == "thinking_delta" {
|
||||
events = append(events, "thinking_delta")
|
||||
}
|
||||
if data.Get("type").String() == "content_block_stop" && data.Get("index").Int() == 0 {
|
||||
events = append(events, "thinking_stop")
|
||||
}
|
||||
if data.Get("type").String() != "content_block_delta" || data.Get("delta.type").String() != "signature_delta" {
|
||||
continue
|
||||
}
|
||||
events = append(events, "signature_delta")
|
||||
signatureDeltaCount++
|
||||
if got := data.Get("delta.signature").String(); got != "enc_sig_final" {
|
||||
t.Fatalf("signature delta = %q, want final done signature", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if signatureDeltaCount != 1 {
|
||||
t.Fatalf("expected one signature_delta, got %d", signatureDeltaCount)
|
||||
}
|
||||
if got, want := strings.Join(events, ","), "thinking_start,thinking_delta,signature_delta,thinking_stop"; got != want {
|
||||
t.Fatalf("thinking event order = %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertCodexResponseToClaude_StreamSignatureOnlyReasoningEmitsThinkingSignature(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
originalRequest := []byte(`{"messages":[]}`)
|
||||
var param any
|
||||
|
||||
chunks := [][]byte{
|
||||
[]byte("data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_123\",\"model\":\"gpt-5\"}}"),
|
||||
[]byte("data: {\"type\":\"response.output_item.added\",\"item\":{\"type\":\"reasoning\",\"encrypted_content\":\"enc_sig_initial\"}}"),
|
||||
[]byte("data: {\"type\":\"response.output_item.done\",\"item\":{\"type\":\"reasoning\",\"encrypted_content\":\"enc_sig_only\"}}"),
|
||||
[]byte("data: {\"type\":\"response.content_part.added\"}"),
|
||||
[]byte("data: {\"type\":\"response.output_text.delta\",\"delta\":\"ok\"}"),
|
||||
}
|
||||
|
||||
var outputs [][]byte
|
||||
for _, chunk := range chunks {
|
||||
outputs = append(outputs, ConvertCodexResponseToClaude(ctx, "", originalRequest, nil, chunk, ¶m)...)
|
||||
}
|
||||
|
||||
thinkingStartFound := false
|
||||
thinkingDeltaFound := false
|
||||
signatureDeltaFound := false
|
||||
thinkingStopFound := false
|
||||
textStartIndex := int64(-1)
|
||||
events := []string{}
|
||||
|
||||
for _, out := range outputs {
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if !strings.HasPrefix(line, "data: ") {
|
||||
continue
|
||||
}
|
||||
data := gjson.Parse(strings.TrimPrefix(line, "data: "))
|
||||
switch data.Get("type").String() {
|
||||
case "content_block_start":
|
||||
if data.Get("content_block.type").String() == "thinking" {
|
||||
events = append(events, "thinking_start")
|
||||
thinkingStartFound = true
|
||||
if got := data.Get("index").Int(); got != 0 {
|
||||
t.Fatalf("thinking block index = %d, want 0", got)
|
||||
}
|
||||
}
|
||||
if data.Get("content_block.type").String() == "text" {
|
||||
events = append(events, "text_start")
|
||||
textStartIndex = data.Get("index").Int()
|
||||
}
|
||||
case "content_block_delta":
|
||||
switch data.Get("delta.type").String() {
|
||||
case "thinking_delta":
|
||||
thinkingDeltaFound = true
|
||||
case "signature_delta":
|
||||
events = append(events, "signature_delta")
|
||||
signatureDeltaFound = true
|
||||
if got := data.Get("index").Int(); got != 0 {
|
||||
t.Fatalf("signature delta index = %d, want 0", got)
|
||||
}
|
||||
if got := data.Get("delta.signature").String(); got != "enc_sig_only" {
|
||||
t.Fatalf("unexpected signature delta: %q", got)
|
||||
}
|
||||
}
|
||||
case "content_block_stop":
|
||||
if data.Get("index").Int() == 0 {
|
||||
events = append(events, "thinking_stop")
|
||||
thinkingStopFound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !thinkingStartFound {
|
||||
t.Fatal("expected signature-only reasoning to start a thinking block")
|
||||
}
|
||||
if thinkingDeltaFound {
|
||||
t.Fatal("did not expect thinking_delta when upstream omitted summary text")
|
||||
}
|
||||
if !signatureDeltaFound {
|
||||
t.Fatal("expected signature_delta from encrypted_content-only reasoning")
|
||||
}
|
||||
if !thinkingStopFound {
|
||||
t.Fatal("expected signature-only thinking block to stop")
|
||||
}
|
||||
if textStartIndex != 1 {
|
||||
t.Fatalf("text block index = %d, want 1 after signature-only thinking block", textStartIndex)
|
||||
}
|
||||
if got, want := strings.Join(events, ","), "thinking_start,signature_delta,thinking_stop,text_start"; got != want {
|
||||
t.Fatalf("signature-only event order = %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertCodexResponseToClaudeNonStream_ThinkingIncludesSignature(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
originalRequest := []byte(`{"messages":[]}`)
|
||||
|
||||
Reference in New Issue
Block a user