diff --git a/internal/conversation/flow/resolver.go b/internal/conversation/flow/resolver.go index 0089baf5..fd5829b4 100644 --- a/internal/conversation/flow/resolver.go +++ b/internal/conversation/flow/resolver.go @@ -179,6 +179,8 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r ReplyTarget: req.ReplyTarget, ConversationType: req.ConversationType, SessionToken: req.ChatToken, + Model: req.Model, + Provider: req.Provider, ReasoningEffort: req.ReasoningEffort, }) if err != nil { @@ -345,6 +347,8 @@ type baseRunConfigParams struct { ConversationType string SessionToken string //nolint:gosec // session credential material, not a hardcoded secret SessionType string + Model string + Provider string ReasoningEffort string // caller-provided override (empty = use bot default) } @@ -364,12 +368,7 @@ func (r *Resolver) buildBaseRunConfig(ctx context.Context, p baseRunConfigParams chatID = p.BotID } - req := conversation.ChatRequest{ - BotID: p.BotID, - ChatID: chatID, - SessionID: p.SessionID, - CurrentChannel: p.CurrentPlatform, - } + req := buildModelSelectionRequest(p, chatID) chatModel, provider, err := r.selectChatModel(ctx, req, botSettings, conversation.Settings{}) if err != nil { @@ -442,6 +441,17 @@ func (r *Resolver) buildBaseRunConfig(ctx context.Context, p baseRunConfigParams return cfg, chatModel, provider, nil } +func buildModelSelectionRequest(p baseRunConfigParams, chatID string) conversation.ChatRequest { + return conversation.ChatRequest{ + BotID: p.BotID, + ChatID: chatID, + SessionID: p.SessionID, + CurrentChannel: p.CurrentPlatform, + Model: p.Model, + Provider: p.Provider, + } +} + // ResolveRunConfig builds a complete RunConfig (model, system prompt, tools, // identity) for a bot+session without loading messages or requiring a query. // The caller is responsible for filling RunConfig.Messages. diff --git a/internal/conversation/flow/resolver_model_selection_test.go b/internal/conversation/flow/resolver_model_selection_test.go index 2c588595..6a1a8f03 100644 --- a/internal/conversation/flow/resolver_model_selection_test.go +++ b/internal/conversation/flow/resolver_model_selection_test.go @@ -57,3 +57,34 @@ func TestMatchesModelReference_TrimmedInput(t *testing.T) { t.Fatal("expected trimmed model slug to match") } } + +func TestBuildModelSelectionRequest_PreservesOverrides(t *testing.T) { + t.Parallel() + + req := buildModelSelectionRequest(baseRunConfigParams{ + BotID: "bot-1", + SessionID: "session-1", + CurrentPlatform: "web", + Model: "model-override", + Provider: "openai-responses", + }, "chat-1") + + if req.BotID != "bot-1" { + t.Fatalf("unexpected bot id: %q", req.BotID) + } + if req.ChatID != "chat-1" { + t.Fatalf("unexpected chat id: %q", req.ChatID) + } + if req.SessionID != "session-1" { + t.Fatalf("unexpected session id: %q", req.SessionID) + } + if req.CurrentChannel != "web" { + t.Fatalf("unexpected current channel: %q", req.CurrentChannel) + } + if req.Model != "model-override" { + t.Fatalf("unexpected model override: %q", req.Model) + } + if req.Provider != "openai-responses" { + t.Fatalf("unexpected provider override: %q", req.Provider) + } +}