269 lines
7.9 KiB
Go
269 lines
7.9 KiB
Go
/*
|
|
* Copyright 2025 CloudWeGo Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package openai
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/cloudwego/eino/internal/generic"
|
|
)
|
|
|
|
func TestConcatResponseMetaExtensions(t *testing.T) {
|
|
t.Run("multiple extensions - takes last non-empty values", func(t *testing.T) {
|
|
err1 := &ResponseError{Code: "err1", Message: "msg1"}
|
|
incomplete := &IncompleteDetails{Reason: "max_tokens"}
|
|
|
|
exts := []*ResponseMetaExtension{
|
|
{
|
|
ID: "id_1",
|
|
Status: "in_progress",
|
|
Error: err1,
|
|
IncompleteDetails: nil,
|
|
},
|
|
{
|
|
ID: "id_2",
|
|
Status: "",
|
|
Error: nil,
|
|
IncompleteDetails: nil,
|
|
},
|
|
{
|
|
ID: "",
|
|
Status: "completed",
|
|
Error: nil,
|
|
IncompleteDetails: incomplete,
|
|
},
|
|
}
|
|
|
|
result, err := ConcatResponseMetaExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "id_2", result.ID)
|
|
assert.Equal(t, ResponseStatus("completed"), result.Status)
|
|
assert.Equal(t, err1, result.Error)
|
|
assert.Equal(t, incomplete, result.IncompleteDetails)
|
|
})
|
|
|
|
t.Run("streaming scenario", func(t *testing.T) {
|
|
exts := []*ResponseMetaExtension{
|
|
{ID: "chatcmpl_stream", Status: "", Error: nil, IncompleteDetails: nil},
|
|
{ID: "", Status: ResponseStatus("in_progress"), Error: nil, IncompleteDetails: nil},
|
|
{ID: "", Status: ResponseStatus("completed"), Error: nil, IncompleteDetails: nil},
|
|
}
|
|
|
|
result, err := ConcatResponseMetaExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "chatcmpl_stream", result.ID)
|
|
assert.Equal(t, ResponseStatus("completed"), result.Status)
|
|
})
|
|
}
|
|
|
|
func TestConcatAssistantGenTextExtensions(t *testing.T) {
|
|
t.Run("single extension with annotations", func(t *testing.T) {
|
|
ext := &AssistantGenTextExtension{
|
|
Annotations: []*TextAnnotation{
|
|
{
|
|
Index: 0,
|
|
Type: "file_citation",
|
|
FileCitation: &TextAnnotationFileCitation{
|
|
FileID: "file_123",
|
|
Filename: "doc.pdf",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result, err := ConcatAssistantGenTextExtensions([]*AssistantGenTextExtension{ext})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Annotations, 1)
|
|
assert.Equal(t, "file_123", result.Annotations[0].FileCitation.FileID)
|
|
})
|
|
|
|
t.Run("multiple extensions - merges annotations by index", func(t *testing.T) {
|
|
exts := []*AssistantGenTextExtension{
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{
|
|
Index: 0,
|
|
Type: "file_citation",
|
|
FileCitation: &TextAnnotationFileCitation{
|
|
FileID: "file_1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{
|
|
Index: 2,
|
|
Type: "url_citation",
|
|
URLCitation: &TextAnnotationURLCitation{
|
|
URL: "https://example.com",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{
|
|
Index: 1,
|
|
Type: "file_path",
|
|
FilePath: &TextAnnotationFilePath{
|
|
FileID: "file_2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result, err := ConcatAssistantGenTextExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Annotations, 3)
|
|
assert.Equal(t, "file_1", result.Annotations[0].FileCitation.FileID)
|
|
assert.Equal(t, "file_2", result.Annotations[1].FilePath.FileID)
|
|
assert.Equal(t, "https://example.com", result.Annotations[2].URLCitation.URL)
|
|
})
|
|
|
|
t.Run("streaming scenario - annotations arrive in chunks", func(t *testing.T) {
|
|
exts := []*AssistantGenTextExtension{
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{Index: 0, Type: "file_citation", FileCitation: &TextAnnotationFileCitation{FileID: "f1"}},
|
|
},
|
|
},
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{Index: 1, Type: "url_citation", URLCitation: &TextAnnotationURLCitation{URL: "url1"}},
|
|
},
|
|
},
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{Index: 2, Type: "file_path", FilePath: &TextAnnotationFilePath{FileID: "f2"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
result, err := ConcatAssistantGenTextExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Annotations, 3)
|
|
assert.Equal(t, "f1", result.Annotations[0].FileCitation.FileID)
|
|
assert.Equal(t, "url1", result.Annotations[1].URLCitation.URL)
|
|
assert.Equal(t, "f2", result.Annotations[2].FilePath.FileID)
|
|
})
|
|
|
|
t.Run("multiple extensions - concatenates refusal reason", func(t *testing.T) {
|
|
ext1 := &AssistantGenTextExtension{Refusal: &OutputRefusal{Reason: "A"}}
|
|
ext2 := &AssistantGenTextExtension{Refusal: &OutputRefusal{Reason: "B"}}
|
|
|
|
result, err := ConcatAssistantGenTextExtensions([]*AssistantGenTextExtension{ext1, ext2})
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result.Refusal)
|
|
assert.Equal(t, "AB", result.Refusal.Reason)
|
|
})
|
|
|
|
t.Run("duplicate index - error occurrence", func(t *testing.T) {
|
|
exts := []*AssistantGenTextExtension{
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{Index: 0, Type: "file_citation", FileCitation: &TextAnnotationFileCitation{FileID: "first"}},
|
|
},
|
|
},
|
|
{
|
|
Annotations: []*TextAnnotation{
|
|
{Index: 0, Type: "url_citation", URLCitation: &TextAnnotationURLCitation{URL: "second"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := ConcatAssistantGenTextExtensions(exts)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestConcatReasoningExtensions(t *testing.T) {
|
|
t.Run("empty chunks - error", func(t *testing.T) {
|
|
_, err := ConcatReasoningExtensions(nil)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("single extension", func(t *testing.T) {
|
|
ext := &ReasoningExtension{
|
|
Content: []*ReasoningContent{
|
|
{Text: "hello", Index: generic.PtrOf(0)},
|
|
},
|
|
}
|
|
|
|
result, err := ConcatReasoningExtensions([]*ReasoningExtension{ext})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Content, 1)
|
|
assert.Equal(t, "hello", result.Content[0].Text)
|
|
})
|
|
|
|
t.Run("streaming scenario - same index concatenated, ordered by index", func(t *testing.T) {
|
|
exts := []*ReasoningExtension{
|
|
{Content: []*ReasoningContent{{Text: "he", Index: generic.PtrOf(0)}}},
|
|
{Content: []*ReasoningContent{{Text: "first", Index: generic.PtrOf(1)}}},
|
|
{Content: []*ReasoningContent{{Text: "llo", Index: generic.PtrOf(0)}}},
|
|
}
|
|
|
|
result, err := ConcatReasoningExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Content, 2)
|
|
assert.Nil(t, result.Content[0].Index)
|
|
assert.Equal(t, "hello", result.Content[0].Text)
|
|
assert.Nil(t, result.Content[1].Index)
|
|
assert.Equal(t, "first", result.Content[1].Text)
|
|
})
|
|
|
|
t.Run("nil extension and nil content skipped", func(t *testing.T) {
|
|
exts := []*ReasoningExtension{
|
|
nil,
|
|
{Content: []*ReasoningContent{nil, {Text: "ok", Index: generic.PtrOf(0)}}},
|
|
}
|
|
|
|
result, err := ConcatReasoningExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Content, 1)
|
|
assert.Equal(t, "ok", result.Content[0].Text)
|
|
})
|
|
|
|
t.Run("all unindexed - appended in arrival order", func(t *testing.T) {
|
|
exts := []*ReasoningExtension{
|
|
{Content: []*ReasoningContent{{Text: "he"}}},
|
|
{Content: []*ReasoningContent{{Text: "llo"}}},
|
|
}
|
|
|
|
result, err := ConcatReasoningExtensions(exts)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Content, 2)
|
|
assert.Nil(t, result.Content[0].Index)
|
|
assert.Equal(t, "he", result.Content[0].Text)
|
|
assert.Nil(t, result.Content[1].Index)
|
|
assert.Equal(t, "llo", result.Content[1].Text)
|
|
})
|
|
|
|
t.Run("mixed indexed and unindexed - error", func(t *testing.T) {
|
|
exts := []*ReasoningExtension{
|
|
{Content: []*ReasoningContent{{Text: "indexed", Index: generic.PtrOf(0)}}},
|
|
{Content: []*ReasoningContent{{Text: "no meta"}}},
|
|
}
|
|
|
|
_, err := ConcatReasoningExtensions(exts)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|