MCPcopy Create free account
hub / github.com/alibaba/open-code-review / RunPerFile

Method RunPerFile

internal/llmloop/loop.go:177–308  ·  view source on GitHub ↗

RunPerFile drives the main LLM conversation loop for a single file. It sends messages with the configured tool definitions, executes any tool calls returned by the model, and collects review comments until task_done is called or limits are reached. Token usage and warnings are aggregated on the Runn

(ctx context.Context, messages []llm.Message, newPath string)

Source from the content-addressed store, hash-verified

175// MainLoopStop return classifies a non-completed, non-error stop at its trigger
176// point so the caller never has to infer the cause from text or context state.
177func (r *Runner) RunPerFile(ctx context.Context, messages []llm.Message, newPath string) (bool, MainLoopStop, error) {
178 toolReqCount := r.deps.Template.MaxToolRequestTimes
179 const maxConsecutiveEmptyRounds = 3
180 consecutiveEmptyRounds := 0
181 sessionID := uuid.NewString()
182
183 // Async compression is owned by this conversation alone; the deferred
184 // cancel aborts any job still in flight when the conversation ends.
185 st := &compressionState{}
186 defer r.cancelPendingCompression(st)
187
188 // stop defaults to StopMaxRounds: if the for-loop exits because toolReqCount
189 // reached zero, the run stopped on the round budget. The empty-round and
190 // compression breaks overwrite it at their trigger points.
191 stop := StopMaxRounds
192 for toolReqCount > 0 {
193 select {
194 case <-ctx.Done():
195 return false, StopNone, ctx.Err()
196 default:
197 }
198
199 toolReqCount--
200
201 fs := r.deps.Session.GetOrCreateFileSession(newPath)
202 rec := fs.AppendTaskRecord(session.MainTask, append([]llm.Message(nil), messages...))
203 startTime := time.Now()
204
205 _, llmSpan := telemetry.StartLLMSpan(ctx, r.deps.Model)
206 resp, err := r.deps.LLMClient.CompletionsWithCtx(ctx, llm.ChatRequest{
207 Model: r.deps.Model,
208 Messages: messages,
209 Tools: r.deps.MainToolDefs,
210 MaxTokens: r.deps.Template.CompletionTokenLimit(),
211 SessionID: sessionID,
212 })
213 duration := time.Since(startTime)
214 if err != nil {
215 rec.SetError(err, duration)
216 telemetry.RecordLLMResult(llmSpan, duration, 0, err)
217 llmSpan.End()
218 telemetry.RecordLLMRequest(ctx, r.deps.Model, duration, 0, "error")
219 return false, StopNone, fmt.Errorf("LLM completion error: %w", err)
220 }
221 rec.SetResponse(resp, duration)
222 totalTokens := int64(0)
223 if resp.Usage != nil {
224 totalTokens = resp.Usage.TotalTokens
225 atomic.AddInt64(&r.totalInputTokens, resp.Usage.PromptTokens)
226 atomic.AddInt64(&r.totalOutputTokens, resp.Usage.CompletionTokens)
227 atomic.AddInt64(&r.totalCacheReadTokens, resp.Usage.CacheReadTokens)
228 atomic.AddInt64(&r.totalCacheWriteTokens, resp.Usage.CacheWriteTokens)
229 }
230 telemetry.RecordLLMResult(llmSpan, duration, totalTokens, nil)
231 llmSpan.End()
232 telemetry.RecordLLMRequest(ctx, r.deps.Model, duration, totalTokens, "ok")
233
234 content := resp.Content()

Calls 15

executeToolCallMethod · 0.95
addNextMessageMethod · 0.95
StartLLMSpanFunction · 0.92
RecordLLMResultFunction · 0.92
RecordLLMRequestFunction · 0.92
WriterFunction · 0.92
NewTextMessageFunction · 0.92
AppendTaskRecordMethod · 0.80
SinceMethod · 0.80
SetErrorMethod · 0.80