diff --git a/sdks/community/go/pkg/core/events/additional_events_test.go b/sdks/community/go/pkg/core/events/additional_events_test.go index 9c2f14765..3baf3bba3 100644 --- a/sdks/community/go/pkg/core/events/additional_events_test.go +++ b/sdks/community/go/pkg/core/events/additional_events_test.go @@ -12,7 +12,7 @@ import ( func TestBaseEventMethods(t *testing.T) { t.Run("ID_Method", func(t *testing.T) { base := NewBaseEvent(EventTypeRunStarted) - + // ID should return a generated ID for base events id := base.ID() assert.NotEmpty(t, id) @@ -21,7 +21,7 @@ func TestBaseEventMethods(t *testing.T) { t.Run("GetBaseEvent_Method", func(t *testing.T) { base := NewBaseEvent(EventTypeRunStarted) - + // GetBaseEvent should return itself result := base.GetBaseEvent() assert.Equal(t, base, result) @@ -29,7 +29,7 @@ func TestBaseEventMethods(t *testing.T) { t.Run("ThreadID_Method", func(t *testing.T) { base := NewBaseEvent(EventTypeRunStarted) - + // ThreadID should return empty string for base events threadID := base.ThreadID() assert.Equal(t, "", threadID) @@ -37,7 +37,7 @@ func TestBaseEventMethods(t *testing.T) { t.Run("RunID_Method", func(t *testing.T) { base := NewBaseEvent(EventTypeRunStarted) - + // RunID should return empty string for base events runID := base.RunID() assert.Equal(t, "", runID) @@ -45,11 +45,11 @@ func TestBaseEventMethods(t *testing.T) { t.Run("ToJSON_Method", func(t *testing.T) { base := NewBaseEvent(EventTypeRunStarted) - + jsonData, err := base.ToJSON() require.NoError(t, err) assert.NotNil(t, jsonData) - + // Verify JSON structure var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) @@ -61,65 +61,49 @@ func TestBaseEventMethods(t *testing.T) { func TestTextMessageChunkEvent(t *testing.T) { t.Run("NewTextMessageChunkEvent", func(t *testing.T) { - event := NewTextMessageChunkEvent() - - assert.Equal(t, EventTypeTextMessageChunk, event.Type()) - assert.Nil(t, event.MessageID) - assert.Nil(t, event.Role) - assert.Nil(t, event.Delta) - }) - - t.Run("WithChunkMessageID", func(t *testing.T) { - event := NewTextMessageChunkEvent().WithChunkMessageID("msg-123") - - assert.NotNil(t, event.MessageID) - assert.Equal(t, "msg-123", *event.MessageID) - }) + messageID := "msg-123" + role := "assistant" + delta := "Hello" + event := NewTextMessageChunkEvent(&messageID, &role, &delta) - t.Run("WithChunkRole", func(t *testing.T) { - event := NewTextMessageChunkEvent().WithChunkRole("assistant") - - assert.NotNil(t, event.Role) - assert.Equal(t, "assistant", *event.Role) - }) - - t.Run("WithChunkDelta", func(t *testing.T) { - event := NewTextMessageChunkEvent().WithChunkDelta("Hello") - - assert.NotNil(t, event.Delta) - assert.Equal(t, "Hello", *event.Delta) + assert.Equal(t, EventTypeTextMessageChunk, event.Type()) + assert.Equal(t, messageID, *event.MessageID) + assert.Equal(t, role, *event.Role) + assert.Equal(t, delta, *event.Delta) }) t.Run("Validate", func(t *testing.T) { + messageID := "msg-123" + role := "user" + delta := "Hello" + // Valid event with all fields - event := NewTextMessageChunkEvent(). - WithChunkMessageID("msg-123"). - WithChunkRole("user"). - WithChunkDelta("Hello") + event := NewTextMessageChunkEvent(&messageID, &role, &delta) assert.NoError(t, event.Validate()) // Valid event with only delta - event = NewTextMessageChunkEvent().WithChunkDelta("Hello") + event = NewTextMessageChunkEvent(nil, nil, &delta) assert.NoError(t, event.Validate()) // Valid - messageID without delta is allowed for chunks - event = NewTextMessageChunkEvent().WithChunkMessageID("msg-123") + event = NewTextMessageChunkEvent(&messageID, nil, nil) assert.NoError(t, event.Validate()) }) t.Run("ToJSON", func(t *testing.T) { - event := NewTextMessageChunkEvent(). - WithChunkMessageID("msg-123"). - WithChunkRole("assistant"). - WithChunkDelta("Hello world") - + messageID := "msg-123" + role := "assistant" + delta := "Hello world" + + event := NewTextMessageChunkEvent(&messageID, &role, &delta) + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeTextMessageChunk), decoded["type"]) assert.Equal(t, "msg-123", decoded["messageId"]) assert.Equal(t, "assistant", decoded["role"]) @@ -130,7 +114,7 @@ func TestTextMessageChunkEvent(t *testing.T) { func TestToolCallChunkEvent(t *testing.T) { t.Run("NewToolCallChunkEvent", func(t *testing.T) { event := NewToolCallChunkEvent() - + assert.Equal(t, EventTypeToolCallChunk, event.Type()) assert.Nil(t, event.ToolCallID) assert.Nil(t, event.ToolCallName) @@ -140,28 +124,28 @@ func TestToolCallChunkEvent(t *testing.T) { t.Run("WithToolCallChunkID", func(t *testing.T) { event := NewToolCallChunkEvent().WithToolCallChunkID("tool-123") - + assert.NotNil(t, event.ToolCallID) assert.Equal(t, "tool-123", *event.ToolCallID) }) t.Run("WithToolCallChunkName", func(t *testing.T) { event := NewToolCallChunkEvent().WithToolCallChunkName("get_weather") - + assert.NotNil(t, event.ToolCallName) assert.Equal(t, "get_weather", *event.ToolCallName) }) t.Run("WithToolCallChunkDelta", func(t *testing.T) { event := NewToolCallChunkEvent().WithToolCallChunkDelta("{\"location\":") - + assert.NotNil(t, event.Delta) assert.Equal(t, "{\"location\":", *event.Delta) }) t.Run("WithToolCallChunkParentMessageID", func(t *testing.T) { event := NewToolCallChunkEvent().WithToolCallChunkParentMessageID("msg-456") - + assert.NotNil(t, event.ParentMessageID) assert.Equal(t, "msg-456", *event.ParentMessageID) }) @@ -190,14 +174,14 @@ func TestToolCallChunkEvent(t *testing.T) { WithToolCallChunkName("search"). WithToolCallChunkDelta("{\"query\":"). WithToolCallChunkParentMessageID("msg-789") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeToolCallChunk), decoded["type"]) assert.Equal(t, "tool-123", decoded["toolCallId"]) assert.Equal(t, "search", decoded["toolCallName"]) @@ -209,7 +193,7 @@ func TestToolCallChunkEvent(t *testing.T) { func TestToolCallResultEvent(t *testing.T) { t.Run("NewToolCallResultEvent", func(t *testing.T) { event := NewToolCallResultEvent("msg-456", "tool-123", "Success") - + assert.Equal(t, EventTypeToolCallResult, event.Type()) assert.Equal(t, "msg-456", event.MessageID) assert.Equal(t, "tool-123", event.ToolCallID) @@ -238,14 +222,14 @@ func TestToolCallResultEvent(t *testing.T) { t.Run("ToJSON", func(t *testing.T) { event := NewToolCallResultEvent("msg-456", "tool-123", "Weather: Sunny, 72°F") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeToolCallResult), decoded["type"]) assert.Equal(t, "msg-456", decoded["messageId"]) assert.Equal(t, "tool-123", decoded["toolCallId"]) @@ -257,91 +241,91 @@ func TestToolCallResultEvent(t *testing.T) { func TestAutoIDGeneration(t *testing.T) { t.Run("TextMessageStartEvent_WithAutoMessageID", func(t *testing.T) { event := NewTextMessageStartEvent("", WithAutoMessageID()) - + assert.NotEmpty(t, event.MessageID) assert.True(t, strings.HasPrefix(event.MessageID, "msg-")) }) t.Run("TextMessageContentEvent_WithAutoMessageIDContent", func(t *testing.T) { event := NewTextMessageContentEventWithOptions("", "Hello", WithAutoMessageIDContent()) - + assert.NotEmpty(t, event.MessageID) assert.True(t, strings.HasPrefix(event.MessageID, "msg-")) }) t.Run("TextMessageEndEvent_WithAutoMessageIDEnd", func(t *testing.T) { event := NewTextMessageEndEventWithOptions("", WithAutoMessageIDEnd()) - + assert.NotEmpty(t, event.MessageID) assert.True(t, strings.HasPrefix(event.MessageID, "msg-")) }) t.Run("ToolCallStartEvent_WithAutoToolCallID", func(t *testing.T) { event := NewToolCallStartEvent("", "get_weather", WithAutoToolCallID()) - + assert.NotEmpty(t, event.ToolCallID) assert.True(t, strings.HasPrefix(event.ToolCallID, "tool-")) }) t.Run("ToolCallArgsEvent_WithAutoToolCallIDArgs", func(t *testing.T) { event := NewToolCallArgsEventWithOptions("", "{}", WithAutoToolCallIDArgs()) - + assert.NotEmpty(t, event.ToolCallID) assert.True(t, strings.HasPrefix(event.ToolCallID, "tool-")) }) t.Run("ToolCallEndEvent_WithAutoToolCallIDEnd", func(t *testing.T) { event := NewToolCallEndEventWithOptions("", WithAutoToolCallIDEnd()) - + assert.NotEmpty(t, event.ToolCallID) assert.True(t, strings.HasPrefix(event.ToolCallID, "tool-")) }) t.Run("RunStartedEvent_WithAutoRunID", func(t *testing.T) { event := NewRunStartedEventWithOptions("thread-123", "", WithAutoRunID()) - + assert.NotEmpty(t, event.RunIDValue) assert.True(t, strings.HasPrefix(event.RunIDValue, "run-")) }) t.Run("RunStartedEvent_WithAutoThreadID", func(t *testing.T) { event := NewRunStartedEventWithOptions("", "run-123", WithAutoThreadID()) - + assert.NotEmpty(t, event.ThreadIDValue) assert.True(t, strings.HasPrefix(event.ThreadIDValue, "thread-")) }) t.Run("RunFinishedEvent_WithAutoRunIDFinished", func(t *testing.T) { event := NewRunFinishedEventWithOptions("thread-123", "", WithAutoRunIDFinished()) - + assert.NotEmpty(t, event.RunIDValue) assert.True(t, strings.HasPrefix(event.RunIDValue, "run-")) }) t.Run("RunFinishedEvent_WithAutoThreadIDFinished", func(t *testing.T) { event := NewRunFinishedEventWithOptions("", "run-123", WithAutoThreadIDFinished()) - + assert.NotEmpty(t, event.ThreadIDValue) assert.True(t, strings.HasPrefix(event.ThreadIDValue, "thread-")) }) t.Run("RunErrorEvent_WithAutoRunIDError", func(t *testing.T) { event := NewRunErrorEvent("Error", WithAutoRunIDError()) - + assert.NotEmpty(t, event.RunIDValue) assert.True(t, strings.HasPrefix(event.RunIDValue, "run-")) }) t.Run("StepStartedEvent_WithAutoStepName", func(t *testing.T) { event := NewStepStartedEventWithOptions("", WithAutoStepName()) - + assert.NotEmpty(t, event.StepName) assert.True(t, strings.HasPrefix(event.StepName, "step-")) }) t.Run("StepFinishedEvent_WithAutoStepNameFinished", func(t *testing.T) { event := NewStepFinishedEventWithOptions("", WithAutoStepNameFinished()) - + assert.NotEmpty(t, event.StepName) assert.True(t, strings.HasPrefix(event.StepName, "step-")) }) @@ -350,7 +334,7 @@ func TestAutoIDGeneration(t *testing.T) { func TestOptionalEventCreators(t *testing.T) { t.Run("NewTextMessageContentEventWithOptions", func(t *testing.T) { event := NewTextMessageContentEventWithOptions("msg-123", "Hello") - + assert.Equal(t, "msg-123", event.MessageID) assert.Equal(t, "Hello", event.Delta) assert.NoError(t, event.Validate()) @@ -358,14 +342,14 @@ func TestOptionalEventCreators(t *testing.T) { t.Run("NewTextMessageEndEventWithOptions", func(t *testing.T) { event := NewTextMessageEndEventWithOptions("msg-123") - + assert.Equal(t, "msg-123", event.MessageID) assert.NoError(t, event.Validate()) }) t.Run("NewToolCallArgsEventWithOptions", func(t *testing.T) { event := NewToolCallArgsEventWithOptions("tool-123", "{}") - + assert.Equal(t, "tool-123", event.ToolCallID) assert.Equal(t, "{}", event.Delta) assert.NoError(t, event.Validate()) @@ -373,14 +357,14 @@ func TestOptionalEventCreators(t *testing.T) { t.Run("NewToolCallEndEventWithOptions", func(t *testing.T) { event := NewToolCallEndEventWithOptions("tool-123") - + assert.Equal(t, "tool-123", event.ToolCallID) assert.NoError(t, event.Validate()) }) t.Run("NewRunStartedEventWithOptions", func(t *testing.T) { event := NewRunStartedEventWithOptions("thread-123", "run-456") - + assert.Equal(t, "thread-123", event.ThreadIDValue) assert.Equal(t, "run-456", event.RunIDValue) assert.NoError(t, event.Validate()) @@ -388,7 +372,7 @@ func TestOptionalEventCreators(t *testing.T) { t.Run("NewRunFinishedEventWithOptions", func(t *testing.T) { event := NewRunFinishedEventWithOptions("thread-123", "run-456") - + assert.Equal(t, "thread-123", event.ThreadIDValue) assert.Equal(t, "run-456", event.RunIDValue) assert.NoError(t, event.Validate()) @@ -396,14 +380,14 @@ func TestOptionalEventCreators(t *testing.T) { t.Run("NewStepStartedEventWithOptions", func(t *testing.T) { event := NewStepStartedEventWithOptions("step-1") - + assert.Equal(t, "step-1", event.StepName) assert.NoError(t, event.Validate()) }) t.Run("NewStepFinishedEventWithOptions", func(t *testing.T) { event := NewStepFinishedEventWithOptions("step-1") - + assert.Equal(t, "step-1", event.StepName) assert.NoError(t, event.Validate()) }) @@ -415,17 +399,17 @@ func TestRunFinishedEvent_WithResult(t *testing.T) { "data": "completed", } event := NewRunFinishedEventWithOptions("thread-123", "run-456", WithResult(result)) - + assert.Equal(t, result, event.Result) - + // Test JSON serialization with result jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.NotNil(t, decoded["result"]) } @@ -434,14 +418,14 @@ func TestStateDeltaEvent_ToJSON(t *testing.T) { {Op: "add", Path: "/field", Value: "value"}, } event := NewStateDeltaEvent(delta) - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeStateDelta), decoded["type"]) assert.NotNil(t, decoded["delta"]) } @@ -455,14 +439,14 @@ func TestMessagesSnapshotEvent_ToJSON(t *testing.T) { }, } event := NewMessagesSnapshotEvent(messages) - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeMessagesSnapshot), decoded["type"]) assert.NotNil(t, decoded["messages"]) } @@ -471,14 +455,14 @@ func TestRawEvent_ToJSON(t *testing.T) { eventData := map[string]interface{}{"key": "value"} source := "external" event := NewRawEvent(eventData, WithSource(source)) - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeRaw), decoded["type"]) assert.NotNil(t, decoded["event"]) assert.Equal(t, source, decoded["source"]) @@ -489,14 +473,14 @@ func TestRunErrorEvent_ToJSON(t *testing.T) { code := "ERR_001" runID := "run-456" event := NewRunErrorEvent(message, WithErrorCode(code), WithRunID(runID)) - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeRunError), decoded["type"]) assert.Equal(t, message, decoded["message"]) assert.Equal(t, code, decoded["code"]) @@ -506,28 +490,28 @@ func TestRunErrorEvent_ToJSON(t *testing.T) { func TestStepEvents_ToJSON(t *testing.T) { t.Run("StepStartedEvent", func(t *testing.T) { event := NewStepStartedEvent("step-1") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeStepStarted), decoded["type"]) assert.Equal(t, "step-1", decoded["stepName"]) }) t.Run("StepFinishedEvent", func(t *testing.T) { event := NewStepFinishedEvent("step-1") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeStepFinished), decoded["type"]) assert.Equal(t, "step-1", decoded["stepName"]) }) @@ -535,28 +519,28 @@ func TestStepEvents_ToJSON(t *testing.T) { func TestTextMessageEndEvent_ToJSON(t *testing.T) { event := NewTextMessageEndEvent("msg-123") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeTextMessageEnd), decoded["type"]) assert.Equal(t, "msg-123", decoded["messageId"]) } func TestToolCallArgsEvent_ToJSON(t *testing.T) { event := NewToolCallArgsEvent("tool-123", "{\"arg\": \"value\"}") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeToolCallArgs), decoded["type"]) assert.Equal(t, "tool-123", decoded["toolCallId"]) assert.Equal(t, "{\"arg\": \"value\"}", decoded["delta"]) @@ -564,14 +548,14 @@ func TestToolCallArgsEvent_ToJSON(t *testing.T) { func TestToolCallEndEvent_ToJSON(t *testing.T) { event := NewToolCallEndEvent("tool-123") - + jsonData, err := event.ToJSON() require.NoError(t, err) - + var decoded map[string]interface{} err = json.Unmarshal(jsonData, &decoded) require.NoError(t, err) - + assert.Equal(t, string(EventTypeToolCallEnd), decoded["type"]) assert.Equal(t, "tool-123", decoded["toolCallId"]) -} \ No newline at end of file +} diff --git a/sdks/community/go/pkg/core/events/decoder.go b/sdks/community/go/pkg/core/events/decoder.go index cf2c0dba7..942d1490d 100644 --- a/sdks/community/go/pkg/core/events/decoder.go +++ b/sdks/community/go/pkg/core/events/decoder.go @@ -60,6 +60,13 @@ func (ed *EventDecoder) DecodeEvent(eventName string, data []byte) (Event, error } return &evt, nil + case EventTypeTextMessageChunk: + var evt TextMessageChunkEvent + if err := json.Unmarshal(data, &evt); err != nil { + return nil, fmt.Errorf("failed to decode TEXT_MESSAGE_CHUNK: %w", err) + } + return &evt, nil + case EventTypeTextMessageContent: var evt TextMessageContentEvent if err := json.Unmarshal(data, &evt); err != nil { diff --git a/sdks/community/go/pkg/core/events/decoder_test.go b/sdks/community/go/pkg/core/events/decoder_test.go index f6ee7091c..058d3a6fb 100644 --- a/sdks/community/go/pkg/core/events/decoder_test.go +++ b/sdks/community/go/pkg/core/events/decoder_test.go @@ -1,7 +1,6 @@ package events import ( - "encoding/json" "testing" "github.com/sirupsen/logrus" @@ -315,20 +314,4 @@ func TestEventDecoder(t *testing.T) { assert.Error(t, err) assert.Nil(t, event) }) - - t.Run("DecodeEvent_UnknownButValidEventType", func(t *testing.T) { - decoder := NewEventDecoder(nil) - data := []byte(`{"some": "data"}`) - - // This should fall through to the default case and return a RawEvent - event, err := decoder.DecodeEvent("TEXT_MESSAGE_CHUNK", data) - require.NoError(t, err) - require.NotNil(t, event) - - rawEvent, ok := event.(*RawEvent) - require.True(t, ok) - assert.Equal(t, EventType("TEXT_MESSAGE_CHUNK"), rawEvent.EventType) - assert.Equal(t, "TEXT_MESSAGE_CHUNK", *rawEvent.Source) - assert.Equal(t, json.RawMessage(data), rawEvent.Event) - }) -} \ No newline at end of file +} diff --git a/sdks/community/go/pkg/core/events/message_events.go b/sdks/community/go/pkg/core/events/message_events.go index c17de766d..63f686294 100644 --- a/sdks/community/go/pkg/core/events/message_events.go +++ b/sdks/community/go/pkg/core/events/message_events.go @@ -195,9 +195,12 @@ type TextMessageChunkEvent struct { } // NewTextMessageChunkEvent creates a new text message chunk event -func NewTextMessageChunkEvent() *TextMessageChunkEvent { +func NewTextMessageChunkEvent(messageID, role, delta *string) *TextMessageChunkEvent { return &TextMessageChunkEvent{ BaseEvent: NewBaseEvent(EventTypeTextMessageChunk), + MessageID: messageID, + Role: role, + Delta: delta, } }