Run executes the chat step-stream loop and delegates persistence/publishing to callbacks.
(ctx context.Context, opts RunOptions)
| 356 | // Run executes the chat step-stream loop and delegates |
| 357 | // persistence/publishing to callbacks. |
| 358 | func Run(ctx context.Context, opts RunOptions) error { |
| 359 | if opts.Model == nil { |
| 360 | return xerrors.New("chat model is required") |
| 361 | } |
| 362 | if opts.PersistStep == nil { |
| 363 | return xerrors.New("persist step callback is required") |
| 364 | } |
| 365 | if opts.MaxSteps <= 0 { |
| 366 | opts.MaxSteps = 1 |
| 367 | } |
| 368 | if opts.StreamSilenceTimeout <= 0 { |
| 369 | opts.StreamSilenceTimeout = defaultStreamSilenceTimeout |
| 370 | } |
| 371 | if opts.Clock == nil { |
| 372 | opts.Clock = quartz.NewReal() |
| 373 | } |
| 374 | if opts.Metrics == nil { |
| 375 | opts.Metrics = NopMetrics() |
| 376 | } |
| 377 | |
| 378 | publishMessagePart := func(role codersdk.ChatMessageRole, part codersdk.ChatMessagePart) { |
| 379 | if opts.PublishMessagePart == nil { |
| 380 | return |
| 381 | } |
| 382 | opts.PublishMessagePart(role, part) |
| 383 | } |
| 384 | |
| 385 | tools := buildToolDefinitions(opts.Tools, opts.ActiveTools, opts.ProviderTools) |
| 386 | |
| 387 | messages := opts.Messages |
| 388 | var lastUsage fantasy.Usage |
| 389 | var lastProviderMetadata fantasy.ProviderMetadata |
| 390 | needsFullHistoryReload := false |
| 391 | reloadFullHistory := func(stage string) error { |
| 392 | if opts.ReloadMessages == nil { |
| 393 | return nil |
| 394 | } |
| 395 | reloaded, err := opts.ReloadMessages(ctx) |
| 396 | if err != nil { |
| 397 | return xerrors.Errorf("reload messages %s: %w", stage, err) |
| 398 | } |
| 399 | messages = reloaded |
| 400 | return nil |
| 401 | } |
| 402 | |
| 403 | totalSteps := 0 |
| 404 | // When totalSteps reaches MaxSteps the inner loop exits immediately |
| 405 | // (its condition is false), stoppedByModel stays false, and the |
| 406 | // post-loop guard breaks the outer compaction loop. |
| 407 | for compactionAttempt := 0; ; compactionAttempt++ { |
| 408 | alreadyCompacted := false |
| 409 | // stoppedByModel is true when the inner step loop |
| 410 | // exited because the model produced no tool calls |
| 411 | // (shouldContinue was false). This distinguishes a |
| 412 | // natural stop from hitting MaxSteps. |
| 413 | stoppedByModel := false |
| 414 | // compactedOnFinalStep tracks whether compaction |
| 415 | // occurred on the very step where the model stopped. |