Add reasoning effort
This commit is contained in:
@@ -450,6 +450,8 @@ type Request struct {
|
||||
SystemMessage string `json:"system_message,omitempty"`
|
||||
// MaxIterations caps the number of agent loop turns (0 = unlimited).
|
||||
MaxIterations int `json:"max_iterations,omitempty"`
|
||||
// ThinkingBudgetTokens is the llama.cpp reasoning budget (-1 = unlimited, 0 = off).
|
||||
ThinkingBudgetTokens *int `json:"thinking_budget_tokens,omitempty"`
|
||||
}
|
||||
|
||||
// Response is the response format for chat and voice endpoints.
|
||||
@@ -545,7 +547,13 @@ func (svc *Service) chat(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
res, err := llmc.Completions(
|
||||
ctx, model, req.SystemMessage, req.MaxIterations, req.Messages,
|
||||
ctx, llm.CompletionsParams{
|
||||
Model: model,
|
||||
SystemMessage: req.SystemMessage,
|
||||
MaxIterations: req.MaxIterations,
|
||||
Messages: req.Messages,
|
||||
ThinkingBudgetTokens: req.ThinkingBudgetTokens,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.ErrorContext(
|
||||
@@ -717,7 +725,13 @@ func (svc *Service) chatStream(w http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
go func() {
|
||||
errs <- llmc.CompletionsStream(
|
||||
ctx, model, req.SystemMessage, req.MaxIterations, req.Messages, events,
|
||||
ctx, llm.CompletionsParams{
|
||||
Model: model,
|
||||
SystemMessage: req.SystemMessage,
|
||||
MaxIterations: req.MaxIterations,
|
||||
Messages: req.Messages,
|
||||
ThinkingBudgetTokens: req.ThinkingBudgetTokens,
|
||||
}, events,
|
||||
)
|
||||
close(errs)
|
||||
}()
|
||||
|
||||
@@ -730,7 +730,7 @@ body {
|
||||
|
||||
.settings-label__sub {
|
||||
display: block;
|
||||
font-size: var(--s-2);
|
||||
font-size: var(--s-1);
|
||||
font-weight: 400;
|
||||
color: var(--color-text-muted);
|
||||
margin-top: 0.125rem;
|
||||
@@ -756,10 +756,6 @@ body {
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.settings-field {
|
||||
margin-bottom: var(--s1);
|
||||
}
|
||||
|
||||
.settings-textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -15,6 +15,7 @@ import { WakeLock } from './wakelock.js';
|
||||
const STREAM_ENDPOINT = '/v1/chat/voice/stream';
|
||||
const SYSTEM_MESSAGE_KEY = 'odidere_system_message';
|
||||
const MAX_TURNS_KEY = 'odidere_max_turns';
|
||||
const REASONING_BUDGET_KEY = 'odidere_reasoning_budget';
|
||||
const MUTE_KEY = 'odidere_mute';
|
||||
const ICONS_URL = '/static/icons.svg';
|
||||
|
||||
@@ -111,6 +112,9 @@ class Odidere {
|
||||
'compose-settings-close',
|
||||
);
|
||||
this.$maxTurnsInput = document.getElementById('max-turns-input');
|
||||
this.$reasoningBudgetInput = document.getElementById(
|
||||
'reasoning-budget-input',
|
||||
);
|
||||
this.$compose = document.querySelector('.compose');
|
||||
|
||||
this.init();
|
||||
@@ -268,9 +272,6 @@ class Odidere {
|
||||
if (e.target === this.$composeSettingsOverlay)
|
||||
this._closeComposeSettings();
|
||||
});
|
||||
this.$maxTurnsInput.addEventListener('input', () => {
|
||||
this._setMaxTurns(this.$maxTurnsInput.value);
|
||||
});
|
||||
|
||||
// --- Model/voice change persistence ---
|
||||
this.settings.$model.addEventListener('change', () =>
|
||||
@@ -640,6 +641,11 @@ class Odidere {
|
||||
payload.max_iterations = maxTurns;
|
||||
}
|
||||
|
||||
const reasoningBudget = this._getReasoningBudget();
|
||||
if (reasoningBudget !== null) {
|
||||
payload.thinking_budget_tokens = reasoningBudget;
|
||||
}
|
||||
|
||||
// Clear attachments and location after building payload.
|
||||
this.clearAttachments();
|
||||
this.clearLocation();
|
||||
@@ -1170,11 +1176,14 @@ class Odidere {
|
||||
|
||||
_openComposeSettings() {
|
||||
this.$maxTurnsInput.value = this._getMaxTurns();
|
||||
this.$reasoningBudgetInput.value = this._getReasoningBudget() ?? -1;
|
||||
this.$composeSettingsOverlay.classList.add('open');
|
||||
this.$composeSettingsClose.focus();
|
||||
}
|
||||
|
||||
_closeComposeSettings() {
|
||||
this._setMaxTurns(this.$maxTurnsInput.value);
|
||||
this._setReasoningBudget(this.$reasoningBudgetInput.value);
|
||||
this.$composeSettingsOverlay.classList.remove('open');
|
||||
this.$composeSettings.focus();
|
||||
}
|
||||
@@ -1188,6 +1197,16 @@ class Odidere {
|
||||
localStorage.setItem(MAX_TURNS_KEY, String(value));
|
||||
}
|
||||
|
||||
_getReasoningBudget() {
|
||||
const stored = localStorage.getItem(REASONING_BUDGET_KEY);
|
||||
if (stored === null) return null;
|
||||
return Number(stored);
|
||||
}
|
||||
|
||||
_setReasoningBudget(value) {
|
||||
localStorage.setItem(REASONING_BUDGET_KEY, String(value));
|
||||
}
|
||||
|
||||
// ====================
|
||||
// TWO-PRESS SAFETY PATTERNS
|
||||
// ====================
|
||||
|
||||
@@ -29,6 +29,24 @@
|
||||
aria-label="Max iterations"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="settings-field">
|
||||
<label class="settings-label" for="reasoning-budget-input">
|
||||
Reasoning Budget
|
||||
<span class="settings-label__sub">
|
||||
Only supported by some models and inference engines.
|
||||
<br>
|
||||
-1 = unlimited, 0 = off · 512 ≈ low · 2048 ≈ med · 8192 ≈ high
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
class="settings-select"
|
||||
id="reasoning-budget-input"
|
||||
value="-1"
|
||||
aria-label="Reasoning budget"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
Reference in New Issue
Block a user