ReLocateComment calls the LLM to regenerate a precise existing_code snippet when text-based matching fails, then retries ResolveComment with the new snippet. Returns (success, response, requestMessages) so the caller can record session history and track token usage. Response and messages are nil on
( ctx context.Context, cm *model.LlmComment, d *model.Diff, client llm.LLMClient, task *template.LlmConversation, modelName string, maxTokens int, )
| 18 | // Returns (success, response, requestMessages) so the caller can record session |
| 19 | // history and track token usage. Response and messages are nil on early exits. |
| 20 | func ReLocateComment( |
| 21 | ctx context.Context, |
| 22 | cm *model.LlmComment, |
| 23 | d *model.Diff, |
| 24 | client llm.LLMClient, |
| 25 | task *template.LlmConversation, |
| 26 | modelName string, |
| 27 | maxTokens int, |
| 28 | ) (bool, *llm.ChatResponse, []llm.Message) { |
| 29 | if task == nil || len(task.Messages) == 0 { |
| 30 | return false, nil, nil |
| 31 | } |
| 32 | |
| 33 | messages := make([]llm.Message, 0, len(task.Messages)) |
| 34 | for _, m := range task.Messages { |
| 35 | content := m.Content |
| 36 | content = strings.ReplaceAll(content, "{diff}", d.Diff) |
| 37 | content = strings.ReplaceAll(content, "{existing_code}", cm.ExistingCode) |
| 38 | content = strings.ReplaceAll(content, "{suggestion_content}", cm.Content) |
| 39 | messages = append(messages, llm.NewTextMessage(m.Role, content)) |
| 40 | } |
| 41 | |
| 42 | startTime := time.Now() |
| 43 | _, llmSpan := telemetry.StartLLMSpan(ctx, modelName) |
| 44 | resp, err := client.CompletionsWithCtx(ctx, llm.ChatRequest{ |
| 45 | Model: modelName, |
| 46 | Messages: messages, |
| 47 | MaxTokens: maxTokens, |
| 48 | }) |
| 49 | duration := time.Since(startTime) |
| 50 | if err != nil { |
| 51 | telemetry.RecordLLMResult(llmSpan, duration, 0, err) |
| 52 | llmSpan.End() |
| 53 | fmt.Fprintf(stdout.Writer(), "[ocr] Re-location LLM call failed for %s: %v\n", cm.Path, err) |
| 54 | return false, nil, messages |
| 55 | } |
| 56 | var totalTokens int64 |
| 57 | if resp.Usage != nil { |
| 58 | totalTokens = resp.Usage.TotalTokens |
| 59 | } |
| 60 | telemetry.RecordLLMResult(llmSpan, duration, totalTokens, nil) |
| 61 | llmSpan.End() |
| 62 | |
| 63 | code := extractCodeBlock(resp.Content()) |
| 64 | if code == "" { |
| 65 | return false, resp, messages |
| 66 | } |
| 67 | |
| 68 | original := cm.ExistingCode |
| 69 | cm.ExistingCode = code |
| 70 | if ResolveComment(cm, d) { |
| 71 | return true, resp, messages |
| 72 | } |
| 73 | cm.ExistingCode = original |
| 74 | return false, resp, messages |
| 75 | } |
| 76 | |
| 77 | // extractCodeBlock extracts the content of the first fenced code block from text. |