From d026c57ad20cdc9826c95477207931e3c7d9ddfe Mon Sep 17 00:00:00 2001 From: Laura Date: Sat, 23 Aug 2025 15:19:43 +0200 Subject: [PATCH] dynamic iterations --- chat.go | 11 ++++++++--- env.go | 12 +++--------- example.config.yml | 2 -- main.go | 4 +++- static/css/chat.css | 13 +++++++++++-- static/css/icons/{ttft.svg => time.svg} | 0 static/index.html | 6 +++++- static/js/chat.js | 26 +++++++++++++++++++------ 8 files changed, 50 insertions(+), 24 deletions(-) rename static/css/icons/{ttft.svg => time.svg} (100%) diff --git a/chat.go b/chat.go index dbdff11..88339ef 100644 --- a/chat.go +++ b/chat.go @@ -42,6 +42,7 @@ type Request struct { Prompt string `json:"prompt"` Model string `json:"model"` Temperature float64 `json:"temperature"` + Iterations int64 `json:"iterations"` JSON bool `json:"json"` Search bool `json:"search"` Reasoning Reasoning `json:"reasoning"` @@ -92,6 +93,10 @@ func (r *Request) Parse() (*openrouter.ChatCompletionRequest, error) { request.Model = r.Model + if r.Iterations < 1 || r.Iterations > 50 { + return nil, fmt.Errorf("invalid iterations (1-50): %d", r.Iterations) + } + if r.Temperature < 0 || r.Temperature > 2 { return nil, fmt.Errorf("invalid temperature (0-2): %f", r.Temperature) } @@ -253,10 +258,10 @@ func HandleChat(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - for iteration := range env.Settings.MaxIterations { - debug("iteration %d of %d", iteration+1, env.Settings.MaxIterations) + for iteration := range raw.Iterations { + debug("iteration %d of %d", iteration+1, raw.Iterations) - if iteration == env.Settings.MaxIterations-1 { + if iteration == raw.Iterations-1 { debug("no more tool calls") request.Tools = nil diff --git a/env.go b/env.go index f02c5b3..c0cf1da 100644 --- a/env.go +++ b/env.go @@ -18,8 +18,7 @@ type EnvTokens struct { } type EnvSettings struct { - CleanContent bool `json:"cleanup"` - MaxIterations uint `json:"iterations"` + CleanContent bool `json:"cleanup"` } type EnvUser struct { @@ -44,8 +43,7 @@ type Environment struct { var env = Environment{ // defaults Settings: EnvSettings{ - CleanContent: true, - MaxIterations: 3, + CleanContent: true, }, } @@ -67,9 +65,6 @@ func (e *Environment) Init() error { log.Warning("Debug mode enabled") } - // check max iterations - e.Settings.MaxIterations = max(e.Settings.MaxIterations, 1) - // check if server secret is set if e.Tokens.Secret == "" { log.Warning("Missing tokens.secret, generating new...") @@ -125,8 +120,7 @@ func (e *Environment) Store() error { "$.tokens.openrouter": {yaml.HeadComment(" openrouter.ai api token (required)")}, "$.tokens.exa": {yaml.HeadComment(" exa search api token (optional; used by search tools)")}, - "$.settings.cleanup": {yaml.HeadComment(" normalize unicode in assistant output (optional; default: true)")}, - "$.settings.iterations": {yaml.HeadComment(" max model turns per request (optional; default: 3)")}, + "$.settings.cleanup": {yaml.HeadComment(" normalize unicode in assistant output (optional; default: true)")}, "$.authentication.enabled": {yaml.HeadComment(" require login with username and password")}, "$.authentication.users": {yaml.HeadComment(" list of users with bcrypt password hashes")}, diff --git a/example.config.yml b/example.config.yml index 9287c5e..aac5508 100644 --- a/example.config.yml +++ b/example.config.yml @@ -12,8 +12,6 @@ tokens: settings: # normalize unicode in assistant output (optional; default: true) cleanup: true - # max model turns per request (optional; default: 3) - iterations: 3 authentication: # require login with username and password diff --git a/main.go b/main.go index 2804d23..2947d1b 100644 --- a/main.go +++ b/main.go @@ -64,8 +64,10 @@ func cache(next http.Handler) http.Handler { path := strings.ToLower(r.URL.Path) ext := filepath.Ext(path) - if ext == ".svg" || ext == ".ttf" || strings.HasSuffix(path, ".min.js") || strings.HasSuffix(path, ".min.css") { + if ext == ".png" || ext == ".svg" || ext == ".ttf" || strings.HasSuffix(path, ".min.js") || strings.HasSuffix(path, ".min.css") { w.Header().Set("Cache-Control", "public, max-age=3024000, immutable") + } else if env.Debug { + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") } next.ServeHTTP(w, r) diff --git a/static/css/chat.css b/static/css/chat.css index 5c053bc..557c462 100644 --- a/static/css/chat.css +++ b/static/css/chat.css @@ -532,7 +532,7 @@ body:not(.loading) #loading { } .statistics .ttft::before { - background-image: url(icons/ttft.svg); + background-image: url(icons/time.svg); } .statistics .tps::before { @@ -789,13 +789,18 @@ input.invalid { } #reasoning-tokens, -#temperature { +#temperature, +#iterations { appearance: textfield; width: 48px; padding: 2px 4px; text-align: right; } +#iterations { + width: 30px; +} + label[for="role"] { background-image: url(icons/user.svg); } @@ -812,6 +817,10 @@ label[for="temperature"] { background-image: url(icons/temperature.svg); } +label[for="iterations"] { + background-image: url(icons/time.svg); +} + label[for="reasoning-effort"] { background-image: url(icons/reasoning.svg); } diff --git a/static/css/icons/ttft.svg b/static/css/icons/time.svg similarity index 100% rename from static/css/icons/ttft.svg rename to static/css/icons/time.svg diff --git a/static/index.html b/static/index.html index d113756..57f7d91 100644 --- a/static/index.html +++ b/static/index.html @@ -58,6 +58,10 @@ +
+ + +
+
diff --git a/static/js/chat.js b/static/js/chat.js index 2943ad2..5e29592 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -9,6 +9,7 @@ $model = document.getElementById("model"), $prompt = document.getElementById("prompt"), $temperature = document.getElementById("temperature"), + $iterations = document.getElementById("iterations"), $reasoningEffort = document.getElementById("reasoning-effort"), $reasoningTokens = document.getElementById("reasoning-tokens"), $json = document.getElementById("json"), @@ -816,20 +817,32 @@ } if (!$temperature.value) { - $temperature.value = 0.85; } - const temperature = parseFloat($temperature.value); + let temperature = parseFloat($temperature.value); if (Number.isNaN(temperature) || temperature < 0 || temperature > 2) { - return; + temperature = 0.85; + + $temperature.value = temperature; } - const effort = $reasoningEffort.value, - tokens = parseInt($reasoningTokens.value); + let iterations = parseInt($iterations.value); + + if (Number.isNaN(iterations) || iterations < 1 || iterations > 50) { + iterations = 3; + + $iterations.value = iterations; + } + + const effort = $reasoningEffort.value; + + let tokens = parseInt($reasoningTokens.value); if (!effort && (Number.isNaN(tokens) || tokens <= 0 || tokens > 1024 * 1024)) { - return; + tokens = 1024; + + $reasoningTokens.value = tokens; } pushMessage(); @@ -842,6 +855,7 @@ prompt: $prompt.value, model: $model.value, temperature: temperature, + iterations: iterations, reasoning: { effort: effort, tokens: tokens || 0,