@@ -1,8 +1,11 @@
11package agent
22
33import (
4+ "cmp"
45 "context"
6+ "encoding/json"
57 "errors"
8+ "log/slog"
69 "slices"
710 "strings"
811
@@ -24,6 +27,7 @@ import (
2427 "github.com/charmbracelet/fantasy/openai"
2528 "github.com/charmbracelet/fantasy/openaicompat"
2629 "github.com/charmbracelet/fantasy/openrouter"
30+ "github.com/qjebbs/go-jsons"
2731)
2832
2933type Coordinator interface {
@@ -103,46 +107,79 @@ func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string,
103107 attachments = nil
104108 }
105109
110+ mergedOptions, temp, topP, topK, freqPenalty, presPenalty := mergeCallOptions(model)
111+
106112 return c.currentAgent.Run(ctx, SessionAgentCall{
107113 SessionID: sessionID,
108114 Prompt: prompt,
109115 Attachments: attachments,
110116 MaxOutputTokens: maxTokens,
111− ProviderOptions: c.getProviderOptions(model),
112− Temperature: model.ModelCfg.Temperature,
113− TopP: model.ModelCfg.TopP,
114− TopK: model.ModelCfg.TopK,
115− FrequencyPenalty: model.ModelCfg.FrequencyPenalty,
116− PresencePenalty: model.ModelCfg.PresencePenalty,
117+ ProviderOptions: mergedOptions,
118+ Temperature: temp,
119+ TopP: topP,
120+ TopK: topK,
121+ FrequencyPenalty: freqPenalty,
122+ PresencePenalty: presPenalty,
117123 })
118124}
119125
120−func (c *coordinator) getProviderOptions(model Model) ai.ProviderOptions {
126+func getProviderOptions(model Model) ai.ProviderOptions {
121127 options := ai.ProviderOptions{}
122128
129+ cfgOpts := "{}"
130+ catwalkOpts := "{}"
131+
132+ if model.ModelCfg.ProviderOptions != nil {
133+ data, err := json.Marshal(model.ModelCfg.ProviderOptions)
134+ if err == nil {
135+ cfgOpts = string(data)
136+ }
137+ }
138+
139+ if model.CatwalkCfg.Options.ProviderOptions != nil {
140+ data, err := json.Marshal(model.CatwalkCfg.Options.ProviderOptions)
141+ if err == nil {
142+ catwalkOpts = string(data)
143+ }
144+ }
145+
146+ got, err := jsons.Merge([]string{catwalkOpts, cfgOpts})
147+ if err != nil {
148+ slog.Error("Could not merge call config", "err", err)
149+ return options
150+ }
151+
152+ mergedOptions := make(map[string]any)
153+
154+ err = json.Unmarshal([]byte(got), &mergedOptions)
155+ if err != nil {
156+ slog.Error("Could not create config for call", "err", err)
157+ return options
158+ }
159+
123160 switch model.Model.Provider() {
124161 case openai.Name:
125− parsed, err := openai.ParseOptions(model.ModelCfg.ProviderOptions)
162+ parsed, err := openai.ParseOptions(mergedOptions)
126163 if err == nil {
127164 options[openai.Name] = parsed
128165 }
129166 case anthropic.Name:
130− parsed, err := anthropic.ParseOptions(model.ModelCfg.ProviderOptions)
167+ parsed, err := anthropic.ParseOptions(mergedOptions)
131168 if err == nil {
132169 options[anthropic.Name] = parsed
133170 }
134171 case openrouter.Name:
135− parsed, err := openrouter.ParseOptions(model.ModelCfg.ProviderOptions)
172+ parsed, err := openrouter.ParseOptions(mergedOptions)
136173 if err == nil {
137174 options[openrouter.Name] = parsed
138175 }
139176 case google.Name:
140− parsed, err := google.ParseOptions(model.ModelCfg.ProviderOptions)
177+ parsed, err := google.ParseOptions(mergedOptions)
141178 if err == nil {
142179 options[google.Name] = parsed
143180 }
144181 case openaicompat.Name:
145− parsed, err := openaicompat.ParseOptions(model.ModelCfg.ProviderOptions)
182+ parsed, err := openaicompat.ParseOptions(mergedOptions)
146183 if err == nil {
147184 options[openaicompat.Name] = parsed
148185 }
@@ -151,6 +188,16 @@ func (c *coordinator) getProviderOptions(model Model) ai.ProviderOptions {
151188 return options
152189}
153190
191+func mergeCallOptions(model Model) (ai.ProviderOptions, *float64, *float64, *int64, *float64, *float64) {
192+ modelOptions := getProviderOptions(model)
193+ temp := cmp.Or(model.ModelCfg.Temperature, model.CatwalkCfg.Options.Temperature)
194+ topP := cmp.Or(model.ModelCfg.TopP, model.CatwalkCfg.Options.TopP)
195+ topK := cmp.Or(model.ModelCfg.TopK, model.CatwalkCfg.Options.TopK)
196+ freqPenalty := cmp.Or(model.ModelCfg.FrequencyPenalty, model.CatwalkCfg.Options.FrequencyPenalty)
197+ presPenalty := cmp.Or(model.ModelCfg.PresencePenalty, model.CatwalkCfg.Options.PresencePenalty)
198+ return modelOptions, temp, topP, topK, freqPenalty, presPenalty
199+}
200+
154201func (c *coordinator) buildAgent(prompt *prompt.Prompt, agent config.Agent) (SessionAgent, error) {
155202 large, small, err := c.buildAgentModels()
156203 if err != nil {