1
0
mirror of https://github.com/coalaura/whiskr.git synced 2025-09-09 09:19:54 +00:00

dynamic iterations

This commit is contained in:
Laura
2025-08-23 15:19:43 +02:00
parent bbe5a54ce1
commit d026c57ad2
8 changed files with 50 additions and 24 deletions

11
chat.go
View File

@@ -42,6 +42,7 @@ type Request struct {
Prompt string `json:"prompt"` Prompt string `json:"prompt"`
Model string `json:"model"` Model string `json:"model"`
Temperature float64 `json:"temperature"` Temperature float64 `json:"temperature"`
Iterations int64 `json:"iterations"`
JSON bool `json:"json"` JSON bool `json:"json"`
Search bool `json:"search"` Search bool `json:"search"`
Reasoning Reasoning `json:"reasoning"` Reasoning Reasoning `json:"reasoning"`
@@ -92,6 +93,10 @@ func (r *Request) Parse() (*openrouter.ChatCompletionRequest, error) {
request.Model = r.Model 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 { if r.Temperature < 0 || r.Temperature > 2 {
return nil, fmt.Errorf("invalid temperature (0-2): %f", r.Temperature) 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() ctx := r.Context()
for iteration := range env.Settings.MaxIterations { for iteration := range raw.Iterations {
debug("iteration %d of %d", iteration+1, env.Settings.MaxIterations) 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") debug("no more tool calls")
request.Tools = nil request.Tools = nil

6
env.go
View File

@@ -19,7 +19,6 @@ type EnvTokens struct {
type EnvSettings struct { type EnvSettings struct {
CleanContent bool `json:"cleanup"` CleanContent bool `json:"cleanup"`
MaxIterations uint `json:"iterations"`
} }
type EnvUser struct { type EnvUser struct {
@@ -45,7 +44,6 @@ var env = Environment{
// defaults // defaults
Settings: EnvSettings{ Settings: EnvSettings{
CleanContent: true, CleanContent: true,
MaxIterations: 3,
}, },
} }
@@ -67,9 +65,6 @@ func (e *Environment) Init() error {
log.Warning("Debug mode enabled") log.Warning("Debug mode enabled")
} }
// check max iterations
e.Settings.MaxIterations = max(e.Settings.MaxIterations, 1)
// check if server secret is set // check if server secret is set
if e.Tokens.Secret == "" { if e.Tokens.Secret == "" {
log.Warning("Missing tokens.secret, generating new...") log.Warning("Missing tokens.secret, generating new...")
@@ -126,7 +121,6 @@ func (e *Environment) Store() error {
"$.tokens.exa": {yaml.HeadComment(" exa search api token (optional; used by search tools)")}, "$.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.cleanup": {yaml.HeadComment(" normalize unicode in assistant output (optional; default: true)")},
"$.settings.iterations": {yaml.HeadComment(" max model turns per request (optional; default: 3)")},
"$.authentication.enabled": {yaml.HeadComment(" require login with username and password")}, "$.authentication.enabled": {yaml.HeadComment(" require login with username and password")},
"$.authentication.users": {yaml.HeadComment(" list of users with bcrypt password hashes")}, "$.authentication.users": {yaml.HeadComment(" list of users with bcrypt password hashes")},

View File

@@ -12,8 +12,6 @@ tokens:
settings: settings:
# normalize unicode in assistant output (optional; default: true) # normalize unicode in assistant output (optional; default: true)
cleanup: true cleanup: true
# max model turns per request (optional; default: 3)
iterations: 3
authentication: authentication:
# require login with username and password # require login with username and password

View File

@@ -64,8 +64,10 @@ func cache(next http.Handler) http.Handler {
path := strings.ToLower(r.URL.Path) path := strings.ToLower(r.URL.Path)
ext := filepath.Ext(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") 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) next.ServeHTTP(w, r)

View File

@@ -532,7 +532,7 @@ body:not(.loading) #loading {
} }
.statistics .ttft::before { .statistics .ttft::before {
background-image: url(icons/ttft.svg); background-image: url(icons/time.svg);
} }
.statistics .tps::before { .statistics .tps::before {
@@ -789,13 +789,18 @@ input.invalid {
} }
#reasoning-tokens, #reasoning-tokens,
#temperature { #temperature,
#iterations {
appearance: textfield; appearance: textfield;
width: 48px; width: 48px;
padding: 2px 4px; padding: 2px 4px;
text-align: right; text-align: right;
} }
#iterations {
width: 30px;
}
label[for="role"] { label[for="role"] {
background-image: url(icons/user.svg); background-image: url(icons/user.svg);
} }
@@ -812,6 +817,10 @@ label[for="temperature"] {
background-image: url(icons/temperature.svg); background-image: url(icons/temperature.svg);
} }
label[for="iterations"] {
background-image: url(icons/time.svg);
}
label[for="reasoning-effort"] { label[for="reasoning-effort"] {
background-image: url(icons/reasoning.svg); background-image: url(icons/reasoning.svg);
} }

View File

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 679 B

View File

@@ -58,6 +58,10 @@
<label for="temperature" title="Temperature (0 - 2)"></label> <label for="temperature" title="Temperature (0 - 2)"></label>
<input id="temperature" type="number" min="0" max="2" step="0.05" value="0.85" /> <input id="temperature" type="number" min="0" max="2" step="0.05" value="0.85" />
</div> </div>
<div class="option">
<label for="iterations" title="Maximum number of iterations (turns) per response"></label>
<input id="iterations" type="number" min="1" max="50" value="3" />
</div>
<div class="option none"> <div class="option none">
<label for="reasoning-effort" title="Reasoning Effort"></label> <label for="reasoning-effort" title="Reasoning Effort"></label>
<select id="reasoning-effort"> <select id="reasoning-effort">
@@ -69,7 +73,7 @@
</div> </div>
<div class="option none"> <div class="option none">
<label for="reasoning-tokens" title="Maximum amount of reasoning tokens"></label> <label for="reasoning-tokens" title="Maximum amount of reasoning tokens"></label>
<input id="reasoning-tokens" type="number" min="2" max="1" step="0.05" value="0.85" /> <input id="reasoning-tokens" type="number" min="2" max="1048576" value="1024" />
</div> </div>
<div class="option group none"> <div class="option group none">
<button id="json" title="Turn on structured json output"></button> <button id="json" title="Turn on structured json output"></button>

View File

@@ -9,6 +9,7 @@
$model = document.getElementById("model"), $model = document.getElementById("model"),
$prompt = document.getElementById("prompt"), $prompt = document.getElementById("prompt"),
$temperature = document.getElementById("temperature"), $temperature = document.getElementById("temperature"),
$iterations = document.getElementById("iterations"),
$reasoningEffort = document.getElementById("reasoning-effort"), $reasoningEffort = document.getElementById("reasoning-effort"),
$reasoningTokens = document.getElementById("reasoning-tokens"), $reasoningTokens = document.getElementById("reasoning-tokens"),
$json = document.getElementById("json"), $json = document.getElementById("json"),
@@ -816,20 +817,32 @@
} }
if (!$temperature.value) { 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) { if (Number.isNaN(temperature) || temperature < 0 || temperature > 2) {
return; temperature = 0.85;
$temperature.value = temperature;
} }
const effort = $reasoningEffort.value, let iterations = parseInt($iterations.value);
tokens = parseInt($reasoningTokens.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)) { if (!effort && (Number.isNaN(tokens) || tokens <= 0 || tokens > 1024 * 1024)) {
return; tokens = 1024;
$reasoningTokens.value = tokens;
} }
pushMessage(); pushMessage();
@@ -842,6 +855,7 @@
prompt: $prompt.value, prompt: $prompt.value,
model: $model.value, model: $model.value,
temperature: temperature, temperature: temperature,
iterations: iterations,
reasoning: { reasoning: {
effort: effort, effort: effort,
tokens: tokens || 0, tokens: tokens || 0,