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

Method RunPerFile

internal/llmloop/loop.go:174–301  ·  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

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