@@ -32,6 +32,7 @@ import (
3232 "charm.land/fantasy/providers/openai"
3333 "charm.land/fantasy/providers/openaicompat"
3434 "charm.land/fantasy/providers/openrouter"
35+ openaisdk "github.com/openai/openai-go/v2/option"
3536 "github.com/qjebbs/go-jsons"
3637)
3738
@@ -118,7 +119,7 @@ func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string,
118119 return nil, errors.New("model provider not configured")
119120 }
120121
121− mergedOptions, temp, topP, topK, freqPenalty, presPenalty := mergeCallOptions(model, providerCfg.Type)
122+ mergedOptions, temp, topP, topK, freqPenalty, presPenalty := mergeCallOptions(model, providerCfg)
122123
123124 return c.currentAgent.Run(ctx, SessionAgentCall{
124125 SessionID: sessionID,
@@ -134,10 +135,11 @@ func (c *coordinator) Run(ctx context.Context, sessionID string, prompt string,
134135 })
135136}
136137
137−func getProviderOptions(model Model, tp catwalk.Type) fantasy.ProviderOptions {
138+func getProviderOptions(model Model, providerCfg config.ProviderConfig) fantasy.ProviderOptions {
138139 options := fantasy.ProviderOptions{}
139140
140141 cfgOpts := []byte("{}")
142+ providerCfgOpts := []byte("{}")
141143 catwalkOpts := []byte("{}")
142144
143145 if model.ModelCfg.ProviderOptions != nil {
@@ -147,6 +149,13 @@ func getProviderOptions(model Model, tp catwalk.Type) fantasy.ProviderOptions {
147149 }
148150 }
149151
152+ if providerCfg.ProviderOptions != nil {
153+ data, err := json.Marshal(providerCfg.ProviderOptions)
154+ if err == nil {
155+ providerCfgOpts = data
156+ }
157+ }
158+
150159 if model.CatwalkCfg.Options.ProviderOptions != nil {
151160 data, err := json.Marshal(model.CatwalkCfg.Options.ProviderOptions)
152161 if err == nil {
@@ -156,6 +165,7 @@ func getProviderOptions(model Model, tp catwalk.Type) fantasy.ProviderOptions {
156165
157166 readers := []io.Reader{
158167 bytes.NewReader(catwalkOpts),
168+ bytes.NewReader(providerCfgOpts),
159169 bytes.NewReader(cfgOpts),
160170 }
161171
@@ -173,7 +183,7 @@ func getProviderOptions(model Model, tp catwalk.Type) fantasy.ProviderOptions {
173183 return options
174184 }
175185
176− switch tp {
186+ switch providerCfg.Type {
177187 case openai.Name:
178188 _, hasReasoningEffort := mergedOptions["reasoning_effort"]
179189 if !hasReasoningEffort && model.ModelCfg.ReasoningEffort != "" {
@@ -255,8 +265,8 @@ func getProviderOptions(model Model, tp catwalk.Type) fantasy.ProviderOptions {
255265 return options
256266}
257267
258−func mergeCallOptions(model Model, tp catwalk.Type) (fantasy.ProviderOptions, *float64, *float64, *int64, *float64, *float64) {
259− modelOptions := getProviderOptions(model, tp)
268+func mergeCallOptions(model Model, cfg config.ProviderConfig) (fantasy.ProviderOptions, *float64, *float64, *int64, *float64, *float64) {
269+ modelOptions := getProviderOptions(model, cfg)
260270 temp := cmp.Or(model.ModelCfg.Temperature, model.CatwalkCfg.Options.Temperature)
261271 topP := cmp.Or(model.ModelCfg.TopP, model.CatwalkCfg.Options.TopP)
262272 topK := cmp.Or(model.ModelCfg.TopK, model.CatwalkCfg.Options.TopK)
@@ -516,7 +526,7 @@ func (c *coordinator) buildOpenrouterProvider(_, apiKey string, headers map[stri
516526 return openrouter.New(opts...)
517527}
518528
519−func (c *coordinator) buildOpenaiCompatProvider(baseURL, apiKey string, headers map[string]string) (fantasy.Provider, error) {
529+func (c *coordinator) buildOpenaiCompatProvider(baseURL, apiKey string, headers map[string]string, extraBody map[string]any) (fantasy.Provider, error) {
520530 opts := []openaicompat.Option{
521531 openaicompat.WithBaseURL(baseURL),
522532 openaicompat.WithAPIKey(apiKey),
@@ -529,6 +539,10 @@ func (c *coordinator) buildOpenaiCompatProvider(baseURL, apiKey string, headers
529539 opts = append(opts, openaicompat.WithHeaders(headers))
530540 }
531541
542+ for extraKey, extraValue := range extraBody {
543+ opts = append(opts, openaicompat.WithSDKOptions(openaisdk.WithJSONSet(extraKey, extraValue)))
544+ }
545+
532546 return openaicompat.New(opts...)
533547}
534548
@@ -646,7 +660,7 @@ func (c *coordinator) buildProvider(providerCfg config.ProviderConfig, model con
646660 case "google-vertex", "vertexai":
647661 return c.buildGoogleVertexProvider(headers, providerCfg.ExtraParams)
648662 case openaicompat.Name:
649− return c.buildOpenaiCompatProvider(baseURL, apiKey, headers)
663+ return c.buildOpenaiCompatProvider(baseURL, apiKey, headers, providerCfg.ExtraBody)
650664 default:
651665 return nil, fmt.Errorf("provider type not supported: %q", providerCfg.Type)
652666 }
@@ -717,5 +731,5 @@ func (c *coordinator) Summarize(ctx context.Context, sessionID string) error {
717731 if !ok {
718732 return errors.New("model provider not configured")
719733 }
720− return c.currentAgent.Summarize(ctx, sessionID, getProviderOptions(c.currentAgent.Model(), providerCfg.Type))
734+ return c.currentAgent.Summarize(ctx, sessionID, getProviderOptions(c.currentAgent.Model(), providerCfg))
721735}