first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,703 @@
package anthropic
import (
"context"
"testing"
"github.com/maximhq/bifrost/core/schemas"
)
// --- isCompactionItem tests ---
func TestIsCompactionItem(t *testing.T) {
t.Parallel()
tests := []struct {
name string
item *schemas.ResponsesMessage
expected bool
}{
{
name: "nil item",
item: nil,
expected: false,
},
{
name: "nil type",
item: &schemas.ResponsesMessage{
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{
{Type: schemas.ResponsesOutputMessageContentTypeCompaction},
},
},
},
expected: false,
},
{
name: "message type with compaction content block",
item: &schemas.ResponsesMessage{
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{
{
Type: schemas.ResponsesOutputMessageContentTypeCompaction,
ResponsesOutputMessageContentCompaction: &schemas.ResponsesOutputMessageContentCompaction{
Summary: "Summary of conversation",
},
},
},
},
},
expected: true,
},
{
name: "message type with text content block",
item: &schemas.ResponsesMessage{
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{
{
Type: schemas.ResponsesOutputMessageContentTypeText,
Text: schemas.Ptr("Hello"),
},
},
},
},
expected: false,
},
{
name: "function call type",
item: &schemas.ResponsesMessage{
Type: schemas.Ptr(schemas.ResponsesMessageTypeFunctionCall),
},
expected: false,
},
{
name: "message type with nil content",
item: &schemas.ResponsesMessage{
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Content: nil,
},
expected: false,
},
{
name: "message type with empty content blocks",
item: &schemas.ResponsesMessage{
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{},
},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isCompactionItem(tt.item)
if result != tt.expected {
t.Errorf("isCompactionItem() = %v, want %v", result, tt.expected)
}
})
}
}
// --- Streaming: Anthropic → Bifrost (inbound) ---
func TestToBifrostResponsesStream_CompactionContentBlockStart(t *testing.T) {
t.Parallel()
state := &AnthropicResponsesStreamState{
ContentIndexToOutputIndex: make(map[int]int),
ContentIndexToBlockType: make(map[int]AnthropicContentBlockType),
ToolArgumentBuffers: make(map[int]string),
MCPCallOutputIndices: make(map[int]bool),
ItemIDs: make(map[int]string),
OutputItems: make(map[int]*schemas.ResponsesMessage),
ReasoningSignatures: make(map[int]string),
TextContentIndices: make(map[int]bool),
ReasoningContentIndices: make(map[int]bool),
CompactionContentIndices: make(map[int]*schemas.CacheControl),
CurrentOutputIndex: 0,
CreatedAt: 1234567890,
HasEmittedCreated: true,
HasEmittedInProgress: true,
}
// content_block_start with compaction type should return nil (defers to delta)
chunk := &AnthropicStreamEvent{
Type: AnthropicStreamEventTypeContentBlockStart,
Index: schemas.Ptr(0),
ContentBlock: &AnthropicContentBlock{
Type: AnthropicContentBlockTypeCompaction,
CacheControl: &schemas.CacheControl{
Type: "ephemeral",
},
},
}
responses, err, isLast := chunk.ToBifrostResponsesStream(context.Background(), 0, state)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if isLast {
t.Error("should not be last chunk")
}
if len(responses) != 0 {
t.Errorf("expected 0 responses for compaction content_block_start, got %d", len(responses))
}
// Verify state was tracked
if _, exists := state.CompactionContentIndices[0]; !exists {
t.Error("expected compaction to be tracked in CompactionContentIndices")
}
if blockType, exists := state.ContentIndexToBlockType[0]; !exists || blockType != AnthropicContentBlockTypeCompaction {
t.Error("expected compaction block type tracked in ContentIndexToBlockType")
}
}
func TestToBifrostResponsesStream_CompactionDelta(t *testing.T) {
t.Parallel()
state := &AnthropicResponsesStreamState{
ContentIndexToOutputIndex: map[int]int{0: 0},
ContentIndexToBlockType: map[int]AnthropicContentBlockType{0: AnthropicContentBlockTypeCompaction},
ToolArgumentBuffers: make(map[int]string),
MCPCallOutputIndices: make(map[int]bool),
ItemIDs: map[int]string{0: "cmp_0"},
OutputItems: make(map[int]*schemas.ResponsesMessage),
ReasoningSignatures: make(map[int]string),
TextContentIndices: make(map[int]bool),
ReasoningContentIndices: make(map[int]bool),
CompactionContentIndices: map[int]*schemas.CacheControl{0: {Type: "ephemeral"}},
CurrentOutputIndex: 1,
CreatedAt: 1234567890,
HasEmittedCreated: true,
HasEmittedInProgress: true,
}
summary := "The user asked about building a website. We discussed HTML, CSS, and JavaScript."
chunk := &AnthropicStreamEvent{
Type: AnthropicStreamEventTypeContentBlockDelta,
Index: schemas.Ptr(0),
Delta: &AnthropicStreamDelta{
Type: AnthropicStreamDeltaTypeCompaction,
Content: &summary,
},
}
responses, err, isLast := chunk.ToBifrostResponsesStream(context.Background(), 0, state)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if isLast {
t.Error("should not be last chunk")
}
// Should emit output_item.added and output_item.done
if len(responses) != 2 {
t.Fatalf("expected 2 responses for compaction delta, got %d", len(responses))
}
// First: output_item.added
added := responses[0]
if added.Type != schemas.ResponsesStreamResponseTypeOutputItemAdded {
t.Errorf("first response type = %v, want %v", added.Type, schemas.ResponsesStreamResponseTypeOutputItemAdded)
}
if added.Item == nil || added.Item.Content == nil || len(added.Item.Content.ContentBlocks) == 0 {
t.Fatal("output_item.added should have content blocks")
}
block := added.Item.Content.ContentBlocks[0]
if block.Type != schemas.ResponsesOutputMessageContentTypeCompaction {
t.Errorf("content block type = %v, want compaction", block.Type)
}
if block.ResponsesOutputMessageContentCompaction == nil {
t.Fatal("expected compaction content to be non-nil")
}
if block.ResponsesOutputMessageContentCompaction.Summary != summary {
t.Errorf("summary = %q, want %q", block.ResponsesOutputMessageContentCompaction.Summary, summary)
}
// Cache control should be preserved from content_block_start
if block.CacheControl == nil || block.CacheControl.Type != "ephemeral" {
t.Error("expected cache control to be preserved")
}
// Second: output_item.done
done := responses[1]
if done.Type != schemas.ResponsesStreamResponseTypeOutputItemDone {
t.Errorf("second response type = %v, want %v", done.Type, schemas.ResponsesStreamResponseTypeOutputItemDone)
}
}
func TestToBifrostResponsesStream_CompactionContentBlockStop(t *testing.T) {
t.Parallel()
state := &AnthropicResponsesStreamState{
ContentIndexToOutputIndex: map[int]int{0: 0},
ContentIndexToBlockType: map[int]AnthropicContentBlockType{0: AnthropicContentBlockTypeCompaction},
ToolArgumentBuffers: make(map[int]string),
MCPCallOutputIndices: make(map[int]bool),
ItemIDs: map[int]string{0: "cmp_0"},
OutputItems: make(map[int]*schemas.ResponsesMessage),
ReasoningSignatures: make(map[int]string),
TextContentIndices: make(map[int]bool),
ReasoningContentIndices: make(map[int]bool),
CompactionContentIndices: make(map[int]*schemas.CacheControl),
CurrentOutputIndex: 1,
CreatedAt: 1234567890,
HasEmittedCreated: true,
HasEmittedInProgress: true,
}
chunk := &AnthropicStreamEvent{
Type: AnthropicStreamEventTypeContentBlockStop,
Index: schemas.Ptr(0),
}
responses, err, isLast := chunk.ToBifrostResponsesStream(context.Background(), 0, state)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if isLast {
t.Error("should not be last chunk")
}
// content_block_stop for compaction should return nil (done was already emitted with delta)
if len(responses) != 0 {
t.Errorf("expected 0 responses for compaction content_block_stop, got %d", len(responses))
}
}
// --- Streaming: Bifrost → Anthropic (outbound, non-passthrough) ---
func TestToAnthropicResponsesStreamResponse_CompactionOutputItemAdded(t *testing.T) {
t.Parallel()
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
summary := "Summary of the conversation about building a website"
bifrostResp := &schemas.BifrostResponsesStreamResponse{
Type: schemas.ResponsesStreamResponseTypeOutputItemAdded,
OutputIndex: schemas.Ptr(0),
Item: &schemas.ResponsesMessage{
ID: schemas.Ptr("cmp_test123"),
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Status: schemas.Ptr("completed"),
Role: schemas.Ptr(schemas.ResponsesInputMessageRoleAssistant),
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{
{
Type: schemas.ResponsesOutputMessageContentTypeCompaction,
ResponsesOutputMessageContentCompaction: &schemas.ResponsesOutputMessageContentCompaction{
Summary: summary,
},
CacheControl: &schemas.CacheControl{Type: "ephemeral"},
},
},
},
},
}
events := ToAnthropicResponsesStreamResponse(ctx, bifrostResp)
// Should emit: content_block_start (compaction) + content_block_delta (compaction_delta)
if len(events) < 2 {
t.Fatalf("expected at least 2 events, got %d", len(events))
}
// Event 1: content_block_start
start := events[0]
if start.Type != AnthropicStreamEventTypeContentBlockStart {
t.Errorf("event[0] type = %v, want content_block_start", start.Type)
}
if start.ContentBlock == nil {
t.Fatal("content_block_start should have ContentBlock")
}
if start.ContentBlock.Type != AnthropicContentBlockTypeCompaction {
t.Errorf("ContentBlock.Type = %v, want compaction", start.ContentBlock.Type)
}
if start.ContentBlock.CacheControl == nil || start.ContentBlock.CacheControl.Type != "ephemeral" {
t.Error("expected cache control to be preserved on content_block_start")
}
// Event 2: content_block_delta with compaction_delta
delta := events[1]
if delta.Type != AnthropicStreamEventTypeContentBlockDelta {
t.Errorf("event[1] type = %v, want content_block_delta", delta.Type)
}
if delta.Delta == nil {
t.Fatal("content_block_delta should have Delta")
}
if delta.Delta.Type != AnthropicStreamDeltaTypeCompaction {
t.Errorf("Delta.Type = %v, want compaction_delta", delta.Delta.Type)
}
if delta.Delta.Content == nil || *delta.Delta.Content != summary {
t.Errorf("Delta.Content = %v, want %q", delta.Delta.Content, summary)
}
}
func TestToAnthropicResponsesStreamResponse_CompactionOutputItemDone(t *testing.T) {
t.Parallel()
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
bifrostResp := &schemas.BifrostResponsesStreamResponse{
Type: schemas.ResponsesStreamResponseTypeOutputItemDone,
OutputIndex: schemas.Ptr(0),
ItemID: schemas.Ptr("cmp_test123"),
Item: &schemas.ResponsesMessage{
ID: schemas.Ptr("cmp_test123"),
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Status: schemas.Ptr("completed"),
Role: schemas.Ptr(schemas.ResponsesInputMessageRoleAssistant),
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{
{
Type: schemas.ResponsesOutputMessageContentTypeCompaction,
ResponsesOutputMessageContentCompaction: &schemas.ResponsesOutputMessageContentCompaction{
Summary: "Summary text",
},
},
},
},
},
}
events := ToAnthropicResponsesStreamResponse(ctx, bifrostResp)
// Should emit content_block_stop
if len(events) != 1 {
t.Fatalf("expected 1 event for output_item.done, got %d", len(events))
}
stop := events[0]
if stop.Type != AnthropicStreamEventTypeContentBlockStop {
t.Errorf("event type = %v, want content_block_stop", stop.Type)
}
}
func TestToAnthropicResponsesStreamResponse_TextOutputItemAdded_NotAffectedByCompactionCheck(t *testing.T) {
t.Parallel()
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
// Regular text message should still emit content_block_start with type=text
bifrostResp := &schemas.BifrostResponsesStreamResponse{
Type: schemas.ResponsesStreamResponseTypeOutputItemAdded,
OutputIndex: schemas.Ptr(0),
Item: &schemas.ResponsesMessage{
ID: schemas.Ptr("msg_test123"),
Type: schemas.Ptr(schemas.ResponsesMessageTypeMessage),
Status: schemas.Ptr("in_progress"),
Role: schemas.Ptr(schemas.ResponsesInputMessageRoleAssistant),
Content: &schemas.ResponsesMessageContent{
ContentBlocks: []schemas.ResponsesMessageContentBlock{
{
Type: schemas.ResponsesOutputMessageContentTypeText,
Text: schemas.Ptr(""),
},
},
},
},
}
events := ToAnthropicResponsesStreamResponse(ctx, bifrostResp)
if len(events) == 0 {
t.Fatal("expected at least 1 event")
}
start := events[0]
if start.Type != AnthropicStreamEventTypeContentBlockStart {
t.Errorf("event type = %v, want content_block_start", start.Type)
}
if start.ContentBlock == nil {
t.Fatal("expected ContentBlock to be non-nil")
}
if start.ContentBlock.Type != AnthropicContentBlockTypeText {
t.Errorf("ContentBlock.Type = %v, want text", start.ContentBlock.Type)
}
}
// --- Non-Streaming: stop_reason mapping ---
func TestToBifrostResponsesResponse_PreservesStopReason(t *testing.T) {
t.Parallel()
tests := []struct {
name string
stopReason AnthropicStopReason
expectedStopReason string
}{
{
name: "compaction stop reason",
stopReason: AnthropicStopReasonCompaction,
expectedStopReason: "compaction",
},
{
name: "end_turn stop reason",
stopReason: AnthropicStopReasonEndTurn,
expectedStopReason: "end_turn",
},
{
name: "tool_use stop reason",
stopReason: AnthropicStopReasonToolUse,
expectedStopReason: "tool_use",
},
{
name: "max_tokens stop reason",
stopReason: AnthropicStopReasonMaxTokens,
expectedStopReason: "max_tokens",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
resp := &AnthropicMessageResponse{
ID: "msg_test",
Type: "message",
Role: "assistant",
Model: "claude-sonnet-4-6",
StopReason: tt.stopReason,
Content: []AnthropicContentBlock{
{Type: AnthropicContentBlockTypeText, Text: schemas.Ptr("Hello")},
},
}
bifrostResp := resp.ToBifrostResponsesResponse(ctx)
if bifrostResp.StopReason == nil {
t.Fatal("expected StopReason to be non-nil")
}
if *bifrostResp.StopReason != tt.expectedStopReason {
t.Errorf("StopReason = %q, want %q", *bifrostResp.StopReason, tt.expectedStopReason)
}
})
}
}
func TestToBifrostResponsesResponse_EmptyStopReason(t *testing.T) {
t.Parallel()
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
resp := &AnthropicMessageResponse{
ID: "msg_test",
Type: "message",
Role: "assistant",
Model: "claude-sonnet-4-6",
Content: []AnthropicContentBlock{},
}
bifrostResp := resp.ToBifrostResponsesResponse(ctx)
if bifrostResp.StopReason != nil {
t.Errorf("expected nil StopReason for empty stop_reason, got %q", *bifrostResp.StopReason)
}
}
func TestToAnthropicResponsesResponse_StopReasonFromBifrost(t *testing.T) {
t.Parallel()
tests := []struct {
name string
stopReason *string
contentBlocks []schemas.ResponsesMessage
expectedReason AnthropicStopReason
}{
{
name: "compaction stop reason from bifrost",
stopReason: schemas.Ptr("compaction"),
expectedReason: AnthropicStopReasonCompaction,
},
{
name: "end_turn mapped from stop",
stopReason: schemas.Ptr("stop"),
expectedReason: AnthropicStopReasonEndTurn,
},
{
name: "tool_use mapped from tool_calls",
stopReason: schemas.Ptr("tool_calls"),
expectedReason: AnthropicStopReasonToolUse,
},
{
name: "nil stop_reason defaults to end_turn",
stopReason: nil,
expectedReason: AnthropicStopReasonEndTurn,
},
{
name: "nil stop_reason with tool_use content defaults to tool_use",
stopReason: nil,
contentBlocks: []schemas.ResponsesMessage{
{
Type: schemas.Ptr(schemas.ResponsesMessageTypeFunctionCall),
ResponsesToolMessage: &schemas.ResponsesToolMessage{
CallID: schemas.Ptr("call_123"),
Name: schemas.Ptr("my_tool"),
},
},
},
expectedReason: AnthropicStopReasonToolUse,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
bifrostResp := &schemas.BifrostResponsesResponse{
ID: schemas.Ptr("resp_test"),
Model: "claude-sonnet-4-6",
StopReason: tt.stopReason,
Output: tt.contentBlocks,
}
result := ToAnthropicResponsesResponse(ctx, bifrostResp)
if result.StopReason != tt.expectedReason {
t.Errorf("StopReason = %v, want %v", result.StopReason, tt.expectedReason)
}
})
}
}
// --- Non-Streaming: compaction content block round-trip ---
func TestCompactionContentBlock_NonStreamingRoundTrip(t *testing.T) {
t.Parallel()
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
summary := "The user requested help building a web scraper using Python with BeautifulSoup."
// Simulate Anthropic response with compaction block
anthropicResp := &AnthropicMessageResponse{
ID: "msg_compaction_test",
Type: "message",
Role: "assistant",
Model: "claude-opus-4-6",
StopReason: AnthropicStopReasonCompaction,
Content: []AnthropicContentBlock{
{
Type: AnthropicContentBlockTypeCompaction,
Content: &AnthropicContent{
ContentStr: &summary,
},
CacheControl: &schemas.CacheControl{Type: "ephemeral"},
},
},
}
// Step 1: Anthropic → Bifrost
bifrostResp := anthropicResp.ToBifrostResponsesResponse(ctx)
if bifrostResp.StopReason == nil || *bifrostResp.StopReason != "compaction" {
t.Fatalf("expected stop_reason='compaction', got %v", bifrostResp.StopReason)
}
if len(bifrostResp.Output) == 0 {
t.Fatal("expected at least one output message")
}
// Find the compaction block
var foundCompaction bool
for _, msg := range bifrostResp.Output {
if msg.Content != nil {
for _, block := range msg.Content.ContentBlocks {
if block.Type == schemas.ResponsesOutputMessageContentTypeCompaction {
foundCompaction = true
if block.ResponsesOutputMessageContentCompaction == nil {
t.Fatal("expected compaction content to be non-nil")
}
if block.ResponsesOutputMessageContentCompaction.Summary != summary {
t.Errorf("summary = %q, want %q", block.ResponsesOutputMessageContentCompaction.Summary, summary)
}
}
}
}
}
if !foundCompaction {
t.Error("compaction block not found in Bifrost output")
}
// Step 2: Bifrost → Anthropic
result := ToAnthropicResponsesResponse(ctx, bifrostResp)
if result.StopReason != AnthropicStopReasonCompaction {
t.Errorf("result StopReason = %v, want compaction", result.StopReason)
}
// Find compaction content block in result
var foundResultCompaction bool
for _, block := range result.Content {
if block.Type == AnthropicContentBlockTypeCompaction {
foundResultCompaction = true
if block.Content == nil || block.Content.ContentStr == nil {
t.Fatal("expected compaction content string")
}
if *block.Content.ContentStr != summary {
t.Errorf("result summary = %q, want %q", *block.Content.ContentStr, summary)
}
if block.CacheControl == nil || block.CacheControl.Type != "ephemeral" {
t.Error("expected cache control to be preserved")
}
}
}
if !foundResultCompaction {
t.Error("compaction block not found in Anthropic result")
}
}
// --- Streaming: compaction stop_reason in response.completed ---
func TestToAnthropicResponsesStreamResponse_CompletedWithCompactionStopReason(t *testing.T) {
t.Parallel()
ctx, cancel := schemas.NewBifrostContextWithCancel(nil)
defer cancel()
bifrostResp := &schemas.BifrostResponsesStreamResponse{
Type: schemas.ResponsesStreamResponseTypeCompleted,
Response: &schemas.BifrostResponsesResponse{
ID: schemas.Ptr("resp_test"),
Model: "claude-opus-4-6",
StopReason: schemas.Ptr("compaction"),
Usage: &schemas.ResponsesResponseUsage{
InputTokens: 1000,
OutputTokens: 500,
TotalTokens: 1500,
},
},
}
events := ToAnthropicResponsesStreamResponse(ctx, bifrostResp)
// Should emit message_delta + message_stop
if len(events) != 2 {
t.Fatalf("expected 2 events for response.completed, got %d", len(events))
}
// message_delta should have stop_reason=compaction
messageDelta := events[0]
if messageDelta.Type != AnthropicStreamEventTypeMessageDelta {
t.Errorf("event[0] type = %v, want message_delta", messageDelta.Type)
}
if messageDelta.Delta == nil || messageDelta.Delta.StopReason == nil {
t.Fatal("expected Delta.StopReason in message_delta")
}
if *messageDelta.Delta.StopReason != AnthropicStopReasonCompaction {
t.Errorf("StopReason = %v, want compaction", *messageDelta.Delta.StopReason)
}
// message_stop
messageStop := events[1]
if messageStop.Type != AnthropicStreamEventTypeMessageStop {
t.Errorf("event[1] type = %v, want message_stop", messageStop.Type)
}
}