add generation params: top_p, top_k, min_p, top_a, repetition_penalty, max_completion_tokens, response_format, reasoning_effort, include_reasoning, stop, verbosity

This commit is contained in:
Levi Neely 2026-05-16 07:32:20 +02:00
parent 6dfae368d0
commit 3867d52689
6 changed files with 136 additions and 41 deletions

View File

@ -78,10 +78,22 @@ func BuildAgentEnv(cfg *config.Config, d tools.Dispatcher, cwd string) AgentEnv
preamble = resolved
}
genParams = backend.GenerationParams{
MaxTokens: cfg.MaxTokens,
Temperature: cfg.Temperature,
FrequencyPenalty: cfg.FrequencyPenalty,
PresencePenalty: cfg.PresencePenalty,
MaxTokens: cfg.MaxTokens,
MaxCompletionTokens: cfg.MaxCompletionTokens,
Temperature: cfg.Temperature,
TopP: cfg.TopP,
TopK: cfg.TopK,
MinP: cfg.MinP,
TopA: cfg.TopA,
FrequencyPenalty: cfg.FrequencyPenalty,
PresencePenalty: cfg.PresencePenalty,
RepetitionPenalty: cfg.RepetitionPenalty,
ThinkingBudget: cfg.Reasoning,
ReasoningEffort: cfg.ReasoningEffort,
IncludeReasoning: cfg.IncludeReasoning,
ResponseFormat: cfg.ResponseFormat,
Stop: cfg.Stop,
Verbosity: cfg.Verbosity,
}
maxSteps = cfg.MaxSteps
if len(cfg.TrustedTools) > 0 {

View File

@ -54,6 +54,9 @@ type anthropicRequest struct {
Tools []anthropicTool `json:"tools,omitempty"`
Stream bool `json:"stream"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
TopK *int `json:"top_k,omitempty"`
StopSeqs []string `json:"stop_sequences,omitempty"`
Thinking *anthropicThinking `json:"thinking,omitempty"`
}
@ -113,6 +116,9 @@ func (b *AnthropicBackend) ChatStream(ctx context.Context, messages []Message, t
Messages: wireMessages,
Stream: true,
Temperature: params.Temperature,
TopP: params.TopP,
TopK: params.TopK,
StopSeqs: params.Stop,
}
if params.ThinkingBudget > 0 {
areq.Thinking = &anthropicThinking{Type: "enabled", BudgetTokens: params.ThinkingBudget}

View File

@ -105,11 +105,22 @@ func (e *ToolUnsupportedError) Error() string { return e.Message }
// GenerationParams controls sampling behaviour for a single ChatStream call.
// Zero values mean "use the API default".
type GenerationParams struct {
MaxTokens int // 0 = no limit
Temperature *float64 // nil = API default
FrequencyPenalty *float64 // nil = API default
PresencePenalty *float64 // nil = API default
ThinkingBudget int // Anthropic extended thinking token budget; 0 = disabled
MaxTokens int // 0 = no limit
MaxCompletionTokens int // OpenAI o-series; 0 = use MaxTokens
Temperature *float64 // nil = API default
TopP *float64 // nil = API default
TopK *int // nil = API default (Anthropic, Ollama)
MinP *float64 // nil = API default (Ollama)
TopA *float64 // nil = API default (Ollama)
FrequencyPenalty *float64 // nil = API default
PresencePenalty *float64 // nil = API default
RepetitionPenalty *float64 // nil = API default (Ollama)
ThinkingBudget int // Anthropic extended thinking token budget; 0 = disabled
ReasoningEffort string // OpenAI o-series: "low", "medium", "high"
IncludeReasoning *bool // include reasoning in response (OpenRouter)
ResponseFormat string // "json_object", "text", etc.
Stop []string // stop sequences
Verbosity string // internal: controls output detail level
}
// Backend is the interface all LLM providers must implement.

View File

@ -134,10 +134,12 @@ type ollamaToolFunction struct {
}
type ollamaChatRequest struct {
Model string `json:"model"`
Messages []ollamaMessage `json:"messages"`
Tools []ollamaTool `json:"tools,omitempty"`
Stream bool `json:"stream"`
Model string `json:"model"`
Messages []ollamaMessage `json:"messages"`
Tools []ollamaTool `json:"tools,omitempty"`
Stream bool `json:"stream"`
Options map[string]interface{} `json:"options,omitempty"`
Stop []string `json:"stop,omitempty"`
}
type ollamaChatResponse struct {
@ -150,7 +152,7 @@ type ollamaChatResponse struct {
// -- implementation --
func (b *OllamaBackend) ChatStream(ctx context.Context, messages []Message, tools []Tool, _ GenerationParams) (<-chan StreamEvent, error) {
func (b *OllamaBackend) ChatStream(ctx context.Context, messages []Message, tools []Tool, params GenerationParams) (<-chan StreamEvent, error) {
model := b.model
wireMessages := make([]ollamaMessage, len(messages))
for i, m := range messages {
@ -174,11 +176,45 @@ func (b *OllamaBackend) ChatStream(ctx context.Context, messages []Message, tool
})
}
opts := make(map[string]interface{})
if params.Temperature != nil {
opts["temperature"] = *params.Temperature
}
if params.TopP != nil {
opts["top_p"] = *params.TopP
}
if params.TopK != nil {
opts["top_k"] = *params.TopK
}
if params.MinP != nil {
opts["min_p"] = *params.MinP
}
if params.TopA != nil {
opts["top_a"] = *params.TopA
}
if params.RepetitionPenalty != nil {
opts["repeat_penalty"] = *params.RepetitionPenalty
}
if params.FrequencyPenalty != nil {
opts["frequency_penalty"] = *params.FrequencyPenalty
}
if params.PresencePenalty != nil {
opts["presence_penalty"] = *params.PresencePenalty
}
if params.MaxTokens > 0 {
opts["num_predict"] = params.MaxTokens
}
if len(opts) == 0 {
opts = nil
}
data, _ := json.Marshal(ollamaChatRequest{
Model: model,
Messages: wireMessages,
Tools: wireTools,
Stream: true,
Options: opts,
Stop: params.Stop,
})
httpReq, _ := http.NewRequestWithContext(ctx, http.MethodPost, b.baseURL.JoinPath("/api/chat").String(), bytes.NewReader(data))

View File

@ -170,15 +170,25 @@ type openAIStreamOptions struct {
}
type openAIChatRequest struct {
Model string `json:"model"`
Messages []openAIMessage `json:"messages"`
Tools []openAITool `json:"tools,omitempty"`
Stream bool `json:"stream"`
StreamOptions *openAIStreamOptions `json:"stream_options,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
Model string `json:"model"`
Messages []openAIMessage `json:"messages"`
Tools []openAITool `json:"tools,omitempty"`
Stream bool `json:"stream"`
StreamOptions *openAIStreamOptions `json:"stream_options,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
Stop []string `json:"stop,omitempty"`
ResponseFormat *openAIResponseFmt `json:"response_format,omitempty"`
ReasoningEffort string `json:"reasoning_effort,omitempty"`
IncludeReasoning *bool `json:"include_reasoning,omitempty"`
}
type openAIResponseFmt struct {
Type string `json:"type"`
}
type openAIUsage struct {
@ -462,15 +472,23 @@ func (b *OpenAIBackend) ChatStream(ctx context.Context, messages []Message, tool
}
}
req := openAIChatRequest{
Model: b.model,
Messages: wireMessages,
Tools: wireTools,
Stream: true,
StreamOptions: &openAIStreamOptions{IncludeUsage: true},
MaxTokens: params.MaxTokens,
Temperature: params.Temperature,
FrequencyPenalty: params.FrequencyPenalty,
PresencePenalty: params.PresencePenalty,
Model: b.model,
Messages: wireMessages,
Tools: wireTools,
Stream: true,
StreamOptions: &openAIStreamOptions{IncludeUsage: true},
MaxTokens: params.MaxTokens,
MaxCompletionTokens: params.MaxCompletionTokens,
Temperature: params.Temperature,
TopP: params.TopP,
FrequencyPenalty: params.FrequencyPenalty,
PresencePenalty: params.PresencePenalty,
Stop: params.Stop,
ReasoningEffort: params.ReasoningEffort,
IncludeReasoning: params.IncludeReasoning,
}
if params.ResponseFormat != "" {
req.ResponseFormat = &openAIResponseFmt{Type: params.ResponseFormat}
}
data, _ := json.Marshal(req)

View File

@ -59,16 +59,28 @@ func (p Prompt) MarshalJSON() ([]byte, error) {
}
type Config struct {
Hooks map[string]HookCmds `json:"hooks,omitempty"`
Prompt Prompt `json:"prompt,omitempty"`
Tools *bool `json:"tools,omitempty"`
TrustedTools []string `json:"trustedTools,omitempty"`
MaxTokens int `json:"maxTokens,omitempty"`
Hooks map[string]HookCmds `json:"hooks,omitempty"`
Prompt Prompt `json:"prompt,omitempty"`
Tools *bool `json:"tools,omitempty"`
TrustedTools []string `json:"trustedTools,omitempty"`
MaxTokens int `json:"maxTokens,omitempty"`
MaxCompletionTokens int `json:"maxCompletionTokens,omitempty"`
// MaxSteps caps the number of tool-call rounds per turn. 0 means unlimited.
MaxSteps int `json:"maxSteps,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
FrequencyPenalty *float64 `json:"frequencyPenalty,omitempty"`
PresencePenalty *float64 `json:"presencePenalty,omitempty"`
MaxSteps int `json:"maxSteps,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"topP,omitempty"`
TopK *int `json:"topK,omitempty"`
MinP *float64 `json:"minP,omitempty"`
TopA *float64 `json:"topA,omitempty"`
FrequencyPenalty *float64 `json:"frequencyPenalty,omitempty"`
PresencePenalty *float64 `json:"presencePenalty,omitempty"`
RepetitionPenalty *float64 `json:"repetitionPenalty,omitempty"`
Reasoning int `json:"reasoning,omitempty"`
ReasoningEffort string `json:"reasoningEffort,omitempty"`
IncludeReasoning *bool `json:"includeReasoning,omitempty"`
ResponseFormat string `json:"responseFormat,omitempty"`
Stop []string `json:"stop,omitempty"`
Verbosity string `json:"verbosity,omitempty"`
}
// Load parses a Config from r.