diff --git a/go.mod b/go.mod index 398560ad01..a014a2d947 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/klauspost/compress v1.18.0 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d - github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20251224033913-b2fbe2627f1c + github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20260113024922-c7feeb806088 github.com/minio/minio-go/v7 v7.0.73 github.com/panjf2000/ants/v2 v2.11.3 // indirect github.com/pingcap/log v1.1.1-0.20221015072633-39906604fb81 // indirect diff --git a/go.sum b/go.sum index 75acd50c19..437a7c7363 100644 --- a/go.sum +++ b/go.sum @@ -799,8 +799,8 @@ github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6 h1:YHMFI6L github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4= -github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20251224033913-b2fbe2627f1c h1:W66Mf/hlR7SWHrSr7xpyt4ACE8v9/C7Y9dMJiZCvp3s= -github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20251224033913-b2fbe2627f1c/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= +github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20260113024922-c7feeb806088 h1:qzlpV+1xygF/XK0bRVoLFg03uAQSCZ2AywZR3wt5ov0= +github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20260113024922-c7feeb806088/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= diff --git a/internal/mocks/flushcommon/mock_util/mock_MsgHandler.go b/internal/mocks/flushcommon/mock_util/mock_MsgHandler.go index f298dfc9ae..934622ccdb 100644 --- a/internal/mocks/flushcommon/mock_util/mock_MsgHandler.go +++ b/internal/mocks/flushcommon/mock_util/mock_MsgHandler.go @@ -353,8 +353,7 @@ func (_c *MockMsgHandler_HandleTruncateCollection_Call) RunAndReturn(run func(me func NewMockMsgHandler(t interface { mock.TestingT Cleanup(func()) -}, -) *MockMsgHandler { +}) *MockMsgHandler { mock := &MockMsgHandler{} mock.Mock.Test(t) diff --git a/internal/mocks/mock_proxy.go b/internal/mocks/mock_proxy.go index 083de51881..b1ac53a2b4 100644 --- a/internal/mocks/mock_proxy.go +++ b/internal/mocks/mock_proxy.go @@ -859,6 +859,65 @@ func (_c *MockProxy_BatchDescribeCollection_Call) RunAndReturn(run func(context. return _c } +// BatchUpdateManifest provides a mock function with given fields: _a0, _a1 +func (_m *MockProxy) BatchUpdateManifest(_a0 context.Context, _a1 *milvuspb.BatchUpdateManifestRequest) (*commonpb.Status, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for BatchUpdateManifest") + } + + var r0 *commonpb.Status + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.BatchUpdateManifestRequest) (*commonpb.Status, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.BatchUpdateManifestRequest) *commonpb.Status); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*commonpb.Status) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.BatchUpdateManifestRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockProxy_BatchUpdateManifest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BatchUpdateManifest' +type MockProxy_BatchUpdateManifest_Call struct { + *mock.Call +} + +// BatchUpdateManifest is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *milvuspb.BatchUpdateManifestRequest +func (_e *MockProxy_Expecter) BatchUpdateManifest(_a0 interface{}, _a1 interface{}) *MockProxy_BatchUpdateManifest_Call { + return &MockProxy_BatchUpdateManifest_Call{Call: _e.mock.On("BatchUpdateManifest", _a0, _a1)} +} + +func (_c *MockProxy_BatchUpdateManifest_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.BatchUpdateManifestRequest)) *MockProxy_BatchUpdateManifest_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*milvuspb.BatchUpdateManifestRequest)) + }) + return _c +} + +func (_c *MockProxy_BatchUpdateManifest_Call) Return(_a0 *commonpb.Status, _a1 error) *MockProxy_BatchUpdateManifest_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockProxy_BatchUpdateManifest_Call) RunAndReturn(run func(context.Context, *milvuspb.BatchUpdateManifestRequest) (*commonpb.Status, error)) *MockProxy_BatchUpdateManifest_Call { + _c.Call.Return(run) + return _c +} + // CalcDistance provides a mock function with given fields: _a0, _a1 func (_m *MockProxy) CalcDistance(_a0 context.Context, _a1 *milvuspb.CalcDistanceRequest) (*milvuspb.CalcDistanceResults, error) { ret := _m.Called(_a0, _a1) diff --git a/internal/proxy/highlighter.go b/internal/proxy/highlighter.go index 3c6f351aca..1e7f9ad280 100644 --- a/internal/proxy/highlighter.go +++ b/internal/proxy/highlighter.go @@ -455,18 +455,23 @@ func (op *semanticHighlightOperator) run(ctx context.Context, span trace.Span, i return nil, errors.Errorf("get highlight failed, text field not in output field %d", fieldID) } texts := fieldDatas.GetScalars().GetStringData().GetData() - highlights, err := op.highlight.Process(ctx, result.Results.GetTopks(), texts) + highlights, scores, err := op.highlight.Process(ctx, result.Results.GetTopks(), texts) if err != nil { return nil, err } - singeFieldHighlights := &commonpb.HighlightResult{ + + if len(highlights) != len(scores) { + return nil, errors.Errorf("Highlights size must equal to scores size, but got highlights size [%d], scores size [%d]", len(highlights), len(scores)) + } + + singleFieldHighlights := &commonpb.HighlightResult{ FieldName: op.highlight.GetFieldName(fieldID), - Datas: make([]*commonpb.HighlightData, 0, len(highlights)), + Datas: make([]*commonpb.HighlightData, len(highlights)), } - for _, highlight := range highlights { - singeFieldHighlights.Datas = append(singeFieldHighlights.Datas, &commonpb.HighlightData{Fragments: highlight}) + for i := range highlights { + singleFieldHighlights.Datas[i] = &commonpb.HighlightData{Fragments: highlights[i], Scores: scores[i]} } - highlightResults = append(highlightResults, singeFieldHighlights) + highlightResults = append(highlightResults, singleFieldHighlights) } result.Results.HighlightResults = highlightResults return []any{result}, nil diff --git a/internal/proxy/search_pipeline_test.go b/internal/proxy/search_pipeline_test.go index 34a331d7a9..d4e424143c 100644 --- a/internal/proxy/search_pipeline_test.go +++ b/internal/proxy/search_pipeline_test.go @@ -368,12 +368,16 @@ func (s *SearchPipelineSuite) TestSemanticHighlightOp() { // Mock SemanticHighlight methods mockProcess := mockey.Mock((*highlight.SemanticHighlight).Process).To( - func(h *highlight.SemanticHighlight, ctx context.Context, topks []int64, texts []string) ([][]string, error) { + func(h *highlight.SemanticHighlight, ctx context.Context, topks []int64, texts []string) ([][]string, [][]float32, error) { return [][]string{ - {"highlighted text 1"}, - {"highlighted text 2"}, - {"highlighted text 3"}, - }, nil + {"highlighted text 1"}, + {"highlighted text 2"}, + {"highlighted text 3"}, + }, [][]float32{ + {0.9}, + {0.8}, + {0.7}, + }, nil }).Build() defer mockProcess.UnPatch() @@ -484,12 +488,15 @@ func (s *SearchPipelineSuite) TestSemanticHighlightOpMultipleFields() { // Use a counter to return different results for different calls callCount := 0 mockProcess := mockey.Mock((*highlight.SemanticHighlight).Process).To( - func(h *highlight.SemanticHighlight, ctx context.Context, topks []int64, texts []string) ([][]string, error) { + func(h *highlight.SemanticHighlight, ctx context.Context, topks []int64, texts []string) ([][]string, [][]float32, error) { callCount++ return [][]string{ - {fmt.Sprintf("highlighted text field%d-1", callCount)}, - {fmt.Sprintf("highlighted text field%d-2", callCount)}, - }, nil + {fmt.Sprintf("highlighted text field%d-1", callCount)}, + {fmt.Sprintf("highlighted text field%d-2", callCount)}, + }, [][]float32{ + {0.9}, + {0.8}, + }, nil }).Build() defer mockProcess.UnPatch() @@ -571,8 +578,8 @@ func (s *SearchPipelineSuite) TestSemanticHighlightOpEmptyResults() { // Mock Process to return empty results mockProcess := mockey.Mock((*highlight.SemanticHighlight).Process).To( - func(h *highlight.SemanticHighlight, ctx context.Context, topks []int64, texts []string) ([][]string, error) { - return [][]string{}, nil + func(h *highlight.SemanticHighlight, ctx context.Context, topks []int64, texts []string) ([][]string, [][]float32, error) { + return [][]string{}, [][]float32{}, nil }).Build() defer mockProcess.UnPatch() diff --git a/internal/util/function/highlight/semantic_highlight.go b/internal/util/function/highlight/semantic_highlight.go index 6c4804423b..b03d291151 100644 --- a/internal/util/function/highlight/semantic_highlight.go +++ b/internal/util/function/highlight/semantic_highlight.go @@ -29,7 +29,7 @@ import ( ) type semanticHighlightProvider interface { - highlight(ctx context.Context, query string, texts []string) ([][]string, error) + highlight(ctx context.Context, query string, texts []string) ([][]string, [][]float32, error) maxBatch() int } @@ -114,40 +114,43 @@ func (highlight *SemanticHighlight) GetFieldName(id int64) string { return highlight.fieldNames[id] } -func (highlight *SemanticHighlight) processOneQuery(ctx context.Context, query string, data []string) ([][]string, error) { - if len(data) == 0 { - return [][]string{}, nil +func (highlight *SemanticHighlight) processOneQuery(ctx context.Context, query string, documents []string) ([][]string, [][]float32, error) { + if len(documents) == 0 { + return [][]string{}, [][]float32{}, nil } - highlights, err := highlight.provider.highlight(ctx, query, data) + highlights, scores, err := highlight.provider.highlight(ctx, query, documents) if err != nil { - return nil, err + return nil, nil, err } - if len(highlights) != len(data) { - return nil, fmt.Errorf("Highlights size must equal to data size, but got highlights size [%d], data size [%d]", len(highlights), len(data)) + if len(highlights) != len(documents) || len(scores) != len(documents) { + return nil, nil, fmt.Errorf("Highlights size must equal to documents size, but got highlights size [%d], scores size [%d], documents size [%d]", len(highlights), len(scores), len(documents)) } - return highlights, nil + + return highlights, scores, nil } -func (highlight *SemanticHighlight) Process(ctx context.Context, topks []int64, data []string) ([][]string, error) { +func (highlight *SemanticHighlight) Process(ctx context.Context, topks []int64, documents []string) ([][]string, [][]float32, error) { nq := len(topks) if len(highlight.queries) != nq { - return nil, fmt.Errorf("nq must equal to queries size, but got nq [%d], queries size [%d], queries: [%v]", nq, len(highlight.queries), highlight.queries) + return nil, nil, fmt.Errorf("nq must equal to queries size, but got nq [%d], queries size [%d], queries: [%v]", nq, len(highlight.queries), highlight.queries) } - if len(data) == 0 { - return [][]string{}, nil + if len(documents) == 0 { + return [][]string{}, [][]float32{}, nil } - highlights := make([][]string, 0, len(data)) + highlights := make([][]string, 0, len(documents)) + scores := make([][]float32, 0, len(documents)) start := int64(0) for i, query := range highlight.queries { size := topks[i] - singleHighlights, err := highlight.processOneQuery(ctx, query, data[start:start+size]) + singleQueryHighlights, singleQueryScores, err := highlight.processOneQuery(ctx, query, documents[start:start+size]) if err != nil { - return nil, err + return nil, nil, err } - highlights = append(highlights, singleHighlights...) + highlights = append(highlights, singleQueryHighlights...) + scores = append(scores, singleQueryScores...) start += size } - return highlights, nil + return highlights, scores, nil } diff --git a/internal/util/function/highlight/semantic_highlight_test.go b/internal/util/function/highlight/semantic_highlight_test.go index 36a4a94ba9..ff06392a79 100644 --- a/internal/util/function/highlight/semantic_highlight_test.go +++ b/internal/util/function/highlight/semantic_highlight_test.go @@ -261,14 +261,18 @@ func (s *SemanticHighlightSuite) TestProcessOneQuery_Success() { {"machine learning"}, {"machine"}, } + expectedScores := [][]float32{ + {0.95}, + {0.80}, + } mock1 := mockey.Mock(zilliz.NewZilliClient).To(func(_ string, _ string, _ string, _ map[string]string) (*zilliz.ZillizClient, error) { return &zilliz.ZillizClient{}, nil }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, error) { - return expectedHighlights, nil + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, [][]float32, error) { + return expectedHighlights, expectedScores, nil }).Build() defer mock2.UnPatch() @@ -292,10 +296,11 @@ func (s *SemanticHighlightSuite) TestProcessOneQuery_Success() { ctx := context.Background() data := []string{"Machine learning is a subset of AI", "Machine learning is powerful"} - highlights, err := highlight.processOneQuery(ctx, "machine learning", data) + highlights, scores, err := highlight.processOneQuery(ctx, "machine learning", data) s.NoError(err) s.Equal(expectedHighlights, highlights) + s.Equal(expectedScores, scores) } func (s *SemanticHighlightSuite) TestProcessOneQuery_Error() { @@ -312,8 +317,8 @@ func (s *SemanticHighlightSuite) TestProcessOneQuery_Error() { }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, error) { - return nil, expectedError + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, [][]float32, error) { + return nil, nil, expectedError }).Build() defer mock2.UnPatch() @@ -337,10 +342,11 @@ func (s *SemanticHighlightSuite) TestProcessOneQuery_Error() { ctx := context.Background() data := []string{"test document"} - highlights, err := highlight.processOneQuery(ctx, "test query", data) + highlights, scores, err := highlight.processOneQuery(ctx, "test query", data) s.Error(err) s.Nil(highlights) + s.Nil(scores) s.Equal(expectedError, err) } @@ -354,9 +360,15 @@ func (s *SemanticHighlightSuite) TestProcess_Success() { expectedHighlights1 := [][]string{ {"machine learning", "deep learning"}, } + expectedScores1 := [][]float32{ + {0.90}, + } expectedHighlights2 := [][]string{ {"deep learning", "machine learning"}, } + expectedScores2 := [][]float32{ + {0.85}, + } callCount := 0 mock1 := mockey.Mock(zilliz.NewZilliClient).To(func(_ string, _ string, _ string, _ map[string]string) (*zilliz.ZillizClient, error) { @@ -364,12 +376,12 @@ func (s *SemanticHighlightSuite) TestProcess_Success() { }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, query string, _ []string, _ map[string]string) ([][]string, error) { + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, query string, _ []string, _ map[string]string) ([][]string, [][]float32, error) { callCount++ if query == "machine learning" { - return expectedHighlights1, nil + return expectedHighlights1, expectedScores1, nil } - return expectedHighlights2, nil + return expectedHighlights2, expectedScores2, nil }).Build() defer mock2.UnPatch() @@ -393,11 +405,15 @@ func (s *SemanticHighlightSuite) TestProcess_Success() { ctx := context.Background() data := []string{"Machine learning document", "Deep learning document"} - highlights, err := highlight.Process(ctx, []int64{1, 1}, data) + highlights, scores, err := highlight.Process(ctx, []int64{1, 1}, data) s.NoError(err) s.NotNil(highlights) s.Equal(2, callCount, "Should call highlight twice for two queries") + s.NotNil(scores) + s.Equal(2, len(scores)) + s.Equal(1, len(scores[0])) + s.Equal(1, len(scores[1])) } func (s *SemanticHighlightSuite) TestProcess_NqMismatch() { @@ -432,11 +448,12 @@ func (s *SemanticHighlightSuite) TestProcess_NqMismatch() { ctx := context.Background() data := []string{"test document"} - highlights, err := highlight.Process(ctx, []int64{1, 1, 1}, data) // nq=3 but queries has only 1 + highlights, scores, err := highlight.Process(ctx, []int64{1, 1, 1}, data) // nq=3 but queries has only 1 s.Error(err) s.Nil(highlights) s.Contains(err.Error(), "nq must equal to queries size") + s.Nil(scores) } func (s *SemanticHighlightSuite) TestProcess_ProviderError() { @@ -453,8 +470,8 @@ func (s *SemanticHighlightSuite) TestProcess_ProviderError() { }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, error) { - return nil, expectedError + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, [][]float32, error) { + return nil, nil, expectedError }).Build() defer mock2.UnPatch() @@ -478,11 +495,12 @@ func (s *SemanticHighlightSuite) TestProcess_ProviderError() { ctx := context.Background() data := []string{"test document"} - highlights, err := highlight.Process(ctx, []int64{1}, data) + highlights, scores, err := highlight.Process(ctx, []int64{1}, data) s.Error(err) s.Nil(highlights) s.Equal(expectedError, err) + s.Nil(scores) } func (s *SemanticHighlightSuite) TestProcess_EmptyData() { @@ -497,8 +515,12 @@ func (s *SemanticHighlightSuite) TestProcess_EmptyData() { }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, texts []string, _ map[string]string) ([][]string, error) { - return [][]string{texts}, nil + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, texts []string, _ map[string]string) ([][]string, [][]float32, error) { + scores := make([][]float32, len(texts)) + for i := range texts { + scores[i] = []float32{0.75} + } + return [][]string{texts}, scores, nil }).Build() defer mock2.UnPatch() @@ -522,18 +544,25 @@ func (s *SemanticHighlightSuite) TestProcess_EmptyData() { ctx := context.Background() data := []string{} - highlights, err := highlight.Process(ctx, []int64{0, 0, 0}, data) + highlights, scores, err := highlight.Process(ctx, []int64{0, 0, 0}, data) s.NoError(err) s.NotNil(highlights) + s.Equal(0, len(highlights)) + s.NotNil(scores) + s.Equal(0, len(scores)) data2 := []string{"test document"} - highlights2, err := highlight.Process(ctx, []int64{0, 1, 0}, data2) + highlights2, scores2, err := highlight.Process(ctx, []int64{0, 1, 0}, data2) s.NoError(err) s.Equal(1, len(highlights2)) s.Equal([][]string{{"test document"}}, highlights2) + s.NotNil(scores2) + s.Equal(1, len(scores2)) + s.Equal(1, len(scores2[0])) + s.Equal(float32(0.75), scores2[0][0]) } func (s *SemanticHighlightSuite) TestBaseSemanticHighlightProvider_MaxBatch() { diff --git a/internal/util/function/highlight/zilliz_highlight_provider.go b/internal/util/function/highlight/zilliz_highlight_provider.go index 7b29f8ec6c..5001df6e8b 100644 --- a/internal/util/function/highlight/zilliz_highlight_provider.go +++ b/internal/util/function/highlight/zilliz_highlight_provider.go @@ -69,10 +69,10 @@ func newZillizHighlightProvider(params []*commonpb.KeyValuePair, conf map[string return &provider, nil } -func (h *zillizHighlightProvider) highlight(ctx context.Context, query string, texts []string) ([][]string, error) { - highlights, err := h.client.Highlight(ctx, query, texts, h.modelParams) +func (h *zillizHighlightProvider) highlight(ctx context.Context, query string, texts []string) ([][]string, [][]float32, error) { + highlights, scores, err := h.client.Highlight(ctx, query, texts, h.modelParams) if err != nil { - return nil, err + return nil, nil, err } - return highlights, nil + return highlights, scores, nil } diff --git a/internal/util/function/highlight/zilliz_highlight_provider_test.go b/internal/util/function/highlight/zilliz_highlight_provider_test.go index 69df3b01dd..b34a87b134 100644 --- a/internal/util/function/highlight/zilliz_highlight_provider_test.go +++ b/internal/util/function/highlight/zilliz_highlight_provider_test.go @@ -177,14 +177,19 @@ func (s *ZillizHighlightProviderSuite) TestZillizHighlightProvider_Highlight_Suc {"Deep learning", "machine learning"}, {"Natural language processing", "machine learning"}, } + expectedScores := [][]float32{ + {0.9, 0.8}, + {0.8, 0.7}, + {0.7, 0.6}, + } mock1 := mockey.Mock(zilliz.NewZilliClient).To(func(_ string, _ string, _ string, _ map[string]string) (*zilliz.ZillizClient, error) { return &zilliz.ZillizClient{}, nil }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, error) { - return expectedHighlights, nil + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, [][]float32, error) { + return expectedHighlights, expectedScores, nil }).Build() defer mock2.UnPatch() @@ -197,10 +202,11 @@ func (s *ZillizHighlightProviderSuite) TestZillizHighlightProvider_Highlight_Suc s.NoError(err) s.NotNil(provider) - highlights, err := provider.highlight(ctx, query, texts) + highlights, scores, err := provider.highlight(ctx, query, texts) s.NoError(err) s.Equal(expectedHighlights, highlights) + s.Equal(expectedScores, scores) } func (s *ZillizHighlightProviderSuite) TestZillizHighlightProvider_Highlight_Error() { @@ -215,8 +221,8 @@ func (s *ZillizHighlightProviderSuite) TestZillizHighlightProvider_Highlight_Err }).Build() defer mock1.UnPatch() - mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, error) { - return nil, expectedError + mock2 := mockey.Mock((*zilliz.ZillizClient).Highlight).To(func(_ *zilliz.ZillizClient, _ context.Context, _ string, _ []string, _ map[string]string) ([][]string, [][]float32, error) { + return nil, nil, expectedError }).Build() defer mock2.UnPatch() @@ -230,9 +236,10 @@ func (s *ZillizHighlightProviderSuite) TestZillizHighlightProvider_Highlight_Err s.NotNil(provider) // Test the highlight method - highlights, err := provider.highlight(ctx, query, texts) + highlights, scores, err := provider.highlight(ctx, query, texts) s.Error(err) s.Nil(highlights) + s.Nil(scores) s.Equal(expectedError, err) } diff --git a/internal/util/function/models/zilliz/zilliz_client.go b/internal/util/function/models/zilliz/zilliz_client.go index d3d1b74d93..73c70cbfb0 100644 --- a/internal/util/function/models/zilliz/zilliz_client.go +++ b/internal/util/function/models/zilliz/zilliz_client.go @@ -253,7 +253,7 @@ func (c *ZillizClient) Rerank(ctx context.Context, query string, texts []string, return res.Scores, nil } -func (c *ZillizClient) Highlight(ctx context.Context, query string, texts []string, params map[string]string) ([][]string, error) { +func (c *ZillizClient) Highlight(ctx context.Context, query string, texts []string, params map[string]string) ([][]string, [][]float32, error) { stub := modelservicepb.NewHighlightServiceClient(c.conn) req := &modelservicepb.HighlightRequest{ Query: query, @@ -263,11 +263,27 @@ func (c *ZillizClient) Highlight(ctx context.Context, query string, texts []stri ctx = c.setMeta(ctx) res, err := stub.Highlight(ctx, req) if err != nil { - return nil, err + return nil, nil, err } highlights := make([][]string, 0, len(res.GetResults())) + scores := make([][]float32, 0, len(res.GetResults())) for _, ret := range res.GetResults() { - highlights = append(highlights, ret.GetSentences()) + sentences := ret.GetSentences() + retScores := ret.GetScores() + + // Handle nil cases + if sentences == nil { + sentences = []string{} + } + if retScores == nil { + retScores = []float32{} + } + + if len(sentences) != len(retScores) { + return nil, nil, fmt.Errorf("sentences length %d does not match scores length %d", len(sentences), len(retScores)) + } + highlights = append(highlights, sentences) + scores = append(scores, retScores) } - return highlights, nil + return highlights, scores, nil } diff --git a/internal/util/function/models/zilliz/zilliz_client_test.go b/internal/util/function/models/zilliz/zilliz_client_test.go index 089f6ddba5..1307825af4 100644 --- a/internal/util/function/models/zilliz/zilliz_client_test.go +++ b/internal/util/function/models/zilliz/zilliz_client_test.go @@ -585,9 +585,11 @@ func TestZillizClient_Highlight(t *testing.T) { Results: []*modelservicepb.HighlightResult{ { Sentences: []string{"highlight1", "highlight2"}, + Scores: []float32{0.9, 0.8}, }, { Sentences: []string{"highlight3", "highlight4"}, + Scores: []float32{0.8, 0.7}, }, }, }, @@ -623,9 +625,10 @@ func TestZillizClient_Highlight(t *testing.T) { query := "test query" texts := []string{"doc1", "doc2", "doc3"} params := map[string]string{"param1": "value1"} - highlights, err := client.Highlight(ctx, query, texts, params) + highlights, scores, err := client.Highlight(ctx, query, texts, params) assert.NoError(t, err) assert.Equal(t, [][]string{{"highlight1", "highlight2"}, {"highlight3", "highlight4"}}, highlights) + assert.Equal(t, [][]float32{{0.9, 0.8}, {0.8, 0.7}}, scores) } func TestZillizClient_Highlight_Error(t *testing.T) { @@ -668,7 +671,130 @@ func TestZillizClient_Highlight_Error(t *testing.T) { query := "test query" texts := []string{"doc1", "doc2", "doc3"} params := map[string]string{"param1": "value1"} - highlights, err := client.Highlight(ctx, query, texts, params) + highlights, scores, err := client.Highlight(ctx, query, texts, params) assert.Error(t, err) assert.Nil(t, highlights) + assert.Nil(t, scores) +} + +func TestZillizClient_Highlight_MismatchLength(t *testing.T) { + tests := []struct { + name string + response *modelservicepb.HighlightResponse + expectedErrMsg string + }{ + { + name: "more sentences than scores", + response: &modelservicepb.HighlightResponse{ + Status: &modelservicepb.Status{Code: 0, Msg: "success"}, + Results: []*modelservicepb.HighlightResult{ + { + Sentences: []string{"highlight1", "highlight2", "highlight3"}, + Scores: []float32{0.9, 0.8}, + }, + }, + }, + expectedErrMsg: "sentences length 3 does not match scores length 2", + }, + { + name: "more scores than sentences", + response: &modelservicepb.HighlightResponse{ + Status: &modelservicepb.Status{Code: 0, Msg: "success"}, + Results: []*modelservicepb.HighlightResult{ + { + Sentences: []string{"highlight1"}, + Scores: []float32{0.9, 0.8, 0.7}, + }, + }, + }, + expectedErrMsg: "sentences length 1 does not match scores length 3", + }, + { + name: "mismatch in second result", + response: &modelservicepb.HighlightResponse{ + Status: &modelservicepb.Status{Code: 0, Msg: "success"}, + Results: []*modelservicepb.HighlightResult{ + { + Sentences: []string{"highlight1", "highlight2"}, + Scores: []float32{0.9, 0.8}, + }, + { + Sentences: []string{"highlight3"}, + Scores: []float32{0.7, 0.6}, + }, + }, + }, + expectedErrMsg: "sentences length 1 does not match scores length 2", + }, + { + name: "nil sentences", + response: &modelservicepb.HighlightResponse{ + Status: &modelservicepb.Status{Code: 0, Msg: "success"}, + Results: []*modelservicepb.HighlightResult{ + {}, + { + Sentences: []string{"highlight3"}, + Scores: []float32{0.7, 0.6}, + }, + }, + }, + expectedErrMsg: "sentences length 1 does not match scores length 2", + }, + { + name: "nil scores", + response: &modelservicepb.HighlightResponse{ + Status: &modelservicepb.Status{Code: 0, Msg: "success"}, + Results: []*modelservicepb.HighlightResult{ + { + Sentences: []string{"highlight1", "highlight2"}, + Scores: nil, + }, + }, + }, + expectedErrMsg: "sentences length 2 does not match scores length 0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s, lis, dialer := setupMockServer(t) + defer lis.Close() + defer s.Stop() + + mockServer := &mockHighlightServer{ + response: tt.response, + } + + modelservicepb.RegisterHighlightServiceServer(s, mockServer) + + go func() { + if err := s.Serve(lis); err != nil { + fmt.Printf("Server exited with error: %v\n", err) + } + }() + + conn, err := grpc.DialContext( + context.Background(), + "bufnet", + grpc.WithContextDialer(dialer), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithBlock(), + ) + require.NoError(t, err) + defer conn.Close() + + client := &ZillizClient{ + modelDeploymentID: "test-deployment", + clusterID: "test-cluster", + conn: conn, + } + + ctx := context.Background() + highlights, scores, err := client.Highlight(ctx, "test query", []string{"doc1"}, map[string]string{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedErrMsg) + assert.Nil(t, highlights) + assert.Nil(t, scores) + }) + } } diff --git a/pkg/go.mod b/pkg/go.mod index 62c32954c3..a4aed3cf19 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -22,7 +22,7 @@ require ( github.com/jolestar/go-commons-pool/v2 v2.1.2 github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 github.com/klauspost/compress v1.18.0 - github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20251224033913-b2fbe2627f1c + github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20260113024922-c7feeb806088 github.com/minio/minio-go/v7 v7.0.73 github.com/panjf2000/ants/v2 v2.11.3 github.com/prometheus/client_golang v1.20.5 diff --git a/pkg/go.sum b/pkg/go.sum index b554ed99c0..e716765bb0 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -482,8 +482,8 @@ github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6 h1:YHMFI6L github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4= -github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20251224033913-b2fbe2627f1c h1:W66Mf/hlR7SWHrSr7xpyt4ACE8v9/C7Y9dMJiZCvp3s= -github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20251224033913-b2fbe2627f1c/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= +github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20260113024922-c7feeb806088 h1:qzlpV+1xygF/XK0bRVoLFg03uAQSCZ2AywZR3wt5ov0= +github.com/milvus-io/milvus-proto/go-api/v2 v2.6.6-0.20260113024922-c7feeb806088/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.73 h1:qr2vi96Qm7kZ4v7LLebjte+MQh621fFWnv93p12htEo= diff --git a/pkg/proto/model_service.proto b/pkg/proto/model_service.proto index e1ee993079..cc25aa3577 100644 --- a/pkg/proto/model_service.proto +++ b/pkg/proto/model_service.proto @@ -82,6 +82,7 @@ message HighlightRequest { message HighlightResult { repeated string sentences = 1; + repeated float scores = 2; } message HighlightResponse { diff --git a/pkg/proto/modelservicepb/model_service.pb.go b/pkg/proto/modelservicepb/model_service.pb.go index b58990e314..d347ce8371 100644 --- a/pkg/proto/modelservicepb/model_service.pb.go +++ b/pkg/proto/modelservicepb/model_service.pb.go @@ -696,7 +696,8 @@ type HighlightResult struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Sentences []string `protobuf:"bytes,1,rep,name=sentences,proto3" json:"sentences,omitempty"` + Sentences []string `protobuf:"bytes,1,rep,name=sentences,proto3" json:"sentences,omitempty"` + Scores []float32 `protobuf:"fixed32,2,rep,packed,name=scores,proto3" json:"scores,omitempty"` } func (x *HighlightResult) Reset() { @@ -738,6 +739,13 @@ func (x *HighlightResult) GetSentences() []string { return nil } +func (x *HighlightResult) GetScores() []float32 { + if x != nil { + return x.Scores + } + return nil +} + type HighlightResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -925,57 +933,58 @@ var file_model_service_proto_rawDesc = []byte{ 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2f, 0x0a, 0x0f, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x47, 0x0a, 0x0f, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x74, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xae, 0x02, 0x0a, 0x11, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, - 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, - 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x5a, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6d, 0x69, 0x6c, - 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, - 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x02, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x22, 0xae, 0x02, + 0x0a, 0x11, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, - 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x45, 0x78, 0x74, 0x72, - 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x86, 0x01, 0x0a, 0x14, 0x54, 0x65, 0x78, 0x74, 0x45, - 0x6d, 0x62, 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x6e, 0x0a, 0x09, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x2e, 0x6d, - 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, - 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6d, 0x62, - 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, - 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6d, - 0x62, 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, - 0x76, 0x0a, 0x0d, 0x52, 0x65, 0x72, 0x61, 0x6e, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x65, 0x0a, 0x06, 0x52, 0x65, 0x72, 0x61, 0x6e, 0x6b, 0x12, 0x2c, 0x2e, 0x6d, 0x69, 0x6c, - 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x52, 0x65, 0x72, 0x61, 0x6e, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x52, 0x65, 0x72, 0x61, 0x6e, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x7a, 0x0a, 0x10, 0x48, 0x69, 0x67, 0x68, 0x6c, - 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x48, - 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x5a, + 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, + 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x07, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6d, 0x69, + 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x1a, 0x3c, 0x0a, 0x0e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x86, + 0x01, 0x0a, 0x14, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6e, 0x0a, 0x09, 0x45, 0x6d, 0x62, 0x65, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x6d, 0x69, 0x6c, 0x76, - 0x75, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x76, 0x0a, 0x0d, 0x52, 0x65, 0x72, 0x61, 0x6e, + 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x65, 0x0a, 0x06, 0x52, 0x65, 0x72, 0x61, + 0x6e, 0x6b, 0x12, 0x2c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, + 0x65, 0x78, 0x74, 0x52, 0x65, 0x72, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2d, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x65, 0x78, + 0x74, 0x52, 0x65, 0x72, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, + 0x7a, 0x0a, 0x10, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x2b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x67, + 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x6c, 0x69, + 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, + 0x2d, 0x69, 0x6f, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x76, + 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var (