11package vector
22
33import (
4+ "bytes"
45 "context"
56 "crypto/md5"
67 "encoding/hex"
8+ "encoding/json"
79 "fmt"
810 "io"
911 "math"
@@ -269,6 +271,72 @@ func (r *weaviateWrapper) unmarshalSimilarSearchResponse(res *models.GraphQLResp
269271 }
270272
271273 items := make ([]* types.SemanticFileItem , 0 , len (results ))
274+
275+ // 如果开启获取源码,则收集所有需要获取的代码片段
276+ var snippets []CodeSnippetRequest
277+ var snippetInfoList []struct {
278+ index int
279+ filePath string
280+ startLine int
281+ endLine int
282+ }
283+
284+ // 第一遍遍历:收集所有需要获取源码的片段信息
285+ for i , result := range results {
286+ obj , ok := result .(map [string ]interface {})
287+ if ! ok {
288+ continue
289+ }
290+
291+ content := getStringValue (obj , Content )
292+ filePath := getStringValue (obj , MetadataFilePath )
293+
294+ // 如果开启获取源码,则从MetadataRange中提取行号信息
295+ if r .cfg .FetchSourceCode && content != "" && filePath != "" && codebasePath != "" {
296+ // 从MetadataRange中提取startLine和endLine
297+ var startLine , endLine int
298+ if rangeValue , ok := obj [MetadataRange ].([]interface {}); ok && len (rangeValue ) >= 2 {
299+ if first , ok := rangeValue [0 ].(float64 ); ok {
300+ startLine = int (first )
301+ }
302+ if second , ok := rangeValue [2 ].(float64 ); ok {
303+ endLine = int (second )
304+ }
305+ }
306+
307+ // 添加到批量获取列表
308+ snippets = append (snippets , CodeSnippetRequest {
309+ FilePath : filePath ,
310+ StartLine : startLine ,
311+ EndLine : endLine ,
312+ })
313+
314+ snippetInfoList = append (snippetInfoList , struct {
315+ index int
316+ filePath string
317+ startLine int
318+ endLine int
319+ }{
320+ index : i ,
321+ filePath : filePath ,
322+ startLine : startLine ,
323+ endLine : endLine ,
324+ })
325+ }
326+ }
327+
328+ // 批量获取代码片段内容
329+ var contentMap map [string ]string
330+ if len (snippets ) > 0 && codebasePath != "" {
331+ var err error
332+ contentMap , err = fetchCodeContentsBatch (context .Background (), r .cfg , clientId , codebasePath , snippets , authorization )
333+ if err != nil {
334+ fmt .Printf ("[DEBUG] 批量获取代码片段失败: %v\n " , err )
335+ contentMap = make (map [string ]string )
336+ }
337+ }
338+
339+ // 第二遍遍历:构建最终的SemanticFileItem列表
272340 for _ , result := range results {
273341 obj , ok := result .(map [string ]interface {})
274342 if ! ok {
@@ -284,9 +352,9 @@ func (r *weaviateWrapper) unmarshalSimilarSearchResponse(res *models.GraphQLResp
284352 content := getStringValue (obj , Content )
285353 filePath := getStringValue (obj , MetadataFilePath )
286354
287- // 如果开启获取源码,则从MetadataRange中提取行号信息
288- if r .cfg .FetchSourceCode && content != "" && filePath != "" {
289- // 从MetadataRange中提取startLine和endLine
355+ // 如果开启获取源码且有批量获取的内容,则使用获取到的内容
356+ if r .cfg .FetchSourceCode && content != "" && filePath != "" && codebasePath != "" {
357+ // 从MetadataRange中提取startLine和endLine(用于构建映射键)
290358 var startLine , endLine int
291359 if rangeValue , ok := obj [MetadataRange ].([]interface {}); ok && len (rangeValue ) >= 2 {
292360 if first , ok := rangeValue [0 ].(float64 ); ok {
@@ -297,12 +365,10 @@ func (r *weaviateWrapper) unmarshalSimilarSearchResponse(res *models.GraphQLResp
297365 }
298366 }
299367
300- // 通过fetchCodeContent接口获取代码片段
301- if codebasePath != "" {
302- fetchedContent , err := fetchCodeContent (context .Background (), r .cfg , clientId , codebasePath , filePath , startLine , endLine , authorization )
303- if err == nil && fetchedContent != "" {
304- content = fetchedContent
305- }
368+ // 构建映射键并查找批量获取的内容
369+ key := fmt .Sprintf ("%s:%d-%d" , filePath , startLine , endLine )
370+ if fetchedContent , exists := contentMap [key ]; exists && fetchedContent != "" {
371+ content = fetchedContent
306372 }
307373 }
308374
@@ -693,6 +759,111 @@ func (r *weaviateWrapper) Query(ctx context.Context, query string, topK int, opt
693759 return rerankedDocs , nil
694760}
695761
762+ // CodeSnippetRequest 代码片段请求结构
763+ type CodeSnippetRequest struct {
764+ FilePath string `json:"filePath"`
765+ StartLine int `json:"startLine"`
766+ EndLine int `json:"endLine"`
767+ }
768+
769+ // CodeSnippetsBatchRequest 批量代码片段请求结构
770+ type CodeSnippetsBatchRequest struct {
771+ ClientId string `json:"clientId"`
772+ WorkspacePath string `json:"workspacePath"`
773+ CodeSnippets []CodeSnippetRequest `json:"codeSnippets"`
774+ }
775+
776+ // CodeSnippetResponse 代码片段响应结构
777+ type CodeSnippetResponse struct {
778+ FilePath string `json:"filePath"`
779+ StartLine int `json:"startLine"`
780+ EndLine int `json:"endLine"`
781+ Content string `json:"content"`
782+ }
783+
784+ // CodeSnippetsBatchResponse 批量代码片段响应结构
785+ type CodeSnippetsBatchResponse struct {
786+ Code string `json:"code"`
787+ Message string `json:"message"`
788+ Success bool `json:"success"`
789+ Data struct {
790+ List []CodeSnippetResponse `json:"list"`
791+ } `json:"data"`
792+ }
793+
794+ // fetchCodeContentsBatch 批量获取代码片段Content
795+ func fetchCodeContentsBatch (ctx context.Context , cfg config.VectorStoreConf , clientId , codebasePath string , snippets []CodeSnippetRequest , authorization string ) (map [string ]string , error ) {
796+ if len (snippets ) == 0 {
797+ return nil , nil
798+ }
799+
800+ // 构建请求体
801+ request := CodeSnippetsBatchRequest {
802+ ClientId : clientId ,
803+ WorkspacePath : codebasePath ,
804+ CodeSnippets : snippets ,
805+ }
806+
807+ jsonData , err := json .Marshal (request )
808+ if err != nil {
809+ return nil , fmt .Errorf ("failed to marshal request: %w" , err )
810+ }
811+
812+ // 构建API请求URL
813+ apiURL := "http://localhost:11380/codebase-indexer/api/v1/snippets/read"
814+
815+ tracer .WithTrace (ctx ).Infof ("fetchCodeContentsBatch: %s" , apiURL )
816+
817+ // 创建HTTP请求
818+ req , err := http .NewRequestWithContext (ctx , "POST" , apiURL , bytes .NewBuffer (jsonData ))
819+ if err != nil {
820+ return nil , fmt .Errorf ("failed to create request: %w" , err )
821+ }
822+
823+ // 设置请求头
824+ req .Header .Set ("Content-Type" , "application/json" )
825+ if authorization != "" {
826+ req .Header .Set ("Authorization" , authorization )
827+ }
828+
829+ // 发送HTTP POST请求
830+ resp , err := http .DefaultClient .Do (req )
831+ if err != nil {
832+ return nil , fmt .Errorf ("failed to fetch code contents batch: %w" , err )
833+ }
834+ defer resp .Body .Close ()
835+
836+ // 检查响应状态码
837+ if resp .StatusCode != http .StatusOK {
838+ return nil , fmt .Errorf ("unexpected status code: %d" , resp .StatusCode )
839+ }
840+
841+ // 读取响应体
842+ body , err := io .ReadAll (resp .Body )
843+ if err != nil {
844+ return nil , fmt .Errorf ("failed to read response body: %w" , err )
845+ }
846+
847+ // 解析响应
848+ var batchResponse CodeSnippetsBatchResponse
849+ if err := json .Unmarshal (body , & batchResponse ); err != nil {
850+ return nil , fmt .Errorf ("failed to unmarshal response: %w" , err )
851+ }
852+
853+ if ! batchResponse .Success {
854+ return nil , fmt .Errorf ("API request failed: %s" , batchResponse .Message )
855+ }
856+
857+ // 构建filePath到content的映射
858+ contentMap := make (map [string ]string )
859+ for _ , snippet := range batchResponse .Data .List {
860+ key := fmt .Sprintf ("%s:%d-%d" , snippet .FilePath , snippet .StartLine , snippet .EndLine )
861+ contentMap [key ] = snippet .Content
862+ }
863+
864+ return contentMap , nil
865+ }
866+
696867// fetchCodeContent 通过API获取代码片段的Content
697868func fetchCodeContent (ctx context.Context , cfg config.VectorStoreConf , clientId , codebasePath , filePath string , startLine , endLine int , authorization string ) (string , error ) {
698869 // 构建API请求URL
0 commit comments