RunAdvisor executes a single, tool-less nested advisor call.
( ctx context.Context, question string, conversationSnapshot []fantasy.Message, opts *RunAdvisorOptions, )
| 23 | |
| 24 | // RunAdvisor executes a single, tool-less nested advisor call. |
| 25 | func (rt *Runtime) RunAdvisor( |
| 26 | ctx context.Context, |
| 27 | question string, |
| 28 | conversationSnapshot []fantasy.Message, |
| 29 | opts *RunAdvisorOptions, |
| 30 | ) (AdvisorResult, error) { |
| 31 | // Model, MaxUsesPerRun, and MaxOutputTokens are validated by NewRuntime. |
| 32 | // Runtime fields are unexported so callers cannot bypass that. |
| 33 | question = strings.TrimSpace(question) |
| 34 | if question == "" { |
| 35 | return AdvisorResult{}, xerrors.New("advisor question is required") |
| 36 | } |
| 37 | question = stringutil.Truncate(question, advisorQuestionMaxRunes) |
| 38 | |
| 39 | if !rt.tryAcquire() { |
| 40 | return AdvisorResult{ |
| 41 | Type: ResultTypeLimitReached, |
| 42 | RemainingUses: 0, |
| 43 | }, nil |
| 44 | } |
| 45 | |
| 46 | // Clone per invocation and reset inherited state so chatloop cannot |
| 47 | // mutate the Runtime's stored options across calls, and so the nested |
| 48 | // call never runs as a chain-mode continuation against stale parent |
| 49 | // state or persists an orphan stored response on the provider side. |
| 50 | nestedProviderOptions := cloneProviderOptions(rt.cfg.ProviderOptions) |
| 51 | resetProviderOptionsForNestedCall(nestedProviderOptions) |
| 52 | |
| 53 | var persistedStep chatloop.PersistedStep |
| 54 | chatLoopOpts := chatloop.RunOptions{ |
| 55 | Model: rt.cfg.Model, |
| 56 | Messages: BuildAdvisorMessages(question, conversationSnapshot), |
| 57 | MaxSteps: 1, |
| 58 | ModelConfig: rt.cfg.ModelConfig, |
| 59 | ProviderOptions: nestedProviderOptions, |
| 60 | PersistStep: func(_ context.Context, step chatloop.PersistedStep) error { |
| 61 | persistedStep = step |
| 62 | return nil |
| 63 | }, |
| 64 | } |
| 65 | if opts != nil && opts.OnAdviceDelta != nil { |
| 66 | chatLoopOpts.PublishMessagePart = func(role codersdk.ChatMessageRole, part codersdk.ChatMessagePart) { |
| 67 | if role != codersdk.ChatMessageRoleAssistant || |
| 68 | part.Type != codersdk.ChatMessagePartTypeText || |
| 69 | part.Text == "" { |
| 70 | return |
| 71 | } |
| 72 | opts.OnAdviceDelta(part.Text) |
| 73 | } |
| 74 | } |
| 75 | if opts != nil && opts.OnAdviceReset != nil { |
| 76 | chatLoopOpts.OnRetry = func(int, error, chatretry.ClassifiedError, time.Duration) { |
| 77 | opts.OnAdviceReset() |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | if err := chatloop.Run(ctx, chatLoopOpts); err != nil { |
| 82 | // Refund the use so a transient provider failure does not |