Add verbosity control (#4837) * Add verbosity control * Remove for Azure OpenAI

965b86da628297e7c39cd434ce901b4c2bfbf8ec

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
4 files changed, +61 -0Showing whitespace changes
public/index.html+16 -0
@@ -2078,6 +2078,22 @@
20782078 </div>
20792079 </div>
20802080 </div>
2081+ <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,openrouter,claude">
2082+ <div class="flex-container oneline-dropdown">
2083+ <label for="openai_verbosity">
2084+ <span data-i18n="Verbosity">Verbosity</span>
2085+ </label>
2086+ <select id="openai_verbosity">
2087+ <option data-i18n="openai_verbosity_auto" value="auto">Auto</option>
2088+ <option data-i18n="openai_verbosity_low" value="low">Low</option>
2089+ <option data-i18n="openai_verbosity_medium" value="medium">Medium</option>
2090+ <option data-i18n="openai_verbosity_high" value="high">High</option>
2091+ </select>
2092+ <div class="toggle-description justifyLeft marginBot5" data-i18n="Constrains the verbosity of the model's response.">
2093+ Constrains the verbosity of the model's response.
2094+ </div>
2095+ </div>
2096+ </div>
20812097 <div class="range-block" data-source="claude">
20822098 <div class="wide100p">
20832099 <div class="flex-container alignItemsCenter">
public/scripts/openai.js+24 -0
@@ -239,6 +239,13 @@ export const reasoning_effort_types = {
239239 max: 'max',
240240};
241241
242+export const verbosity_levels = {
243+ auto: 'auto',
244+ low: 'low',
245+ medium: 'medium',
246+ high: 'high',
247+};
248+
242249export const ZAI_ENDPOINT = {
243250 COMMON: 'common',
244251 CODING: 'coding',
@@ -342,6 +349,7 @@ export const settingsToUpdate = {
342349 function_calling: ['#openai_function_calling', 'function_calling', true, false],
343350 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false],
344351 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false],
352+ verbosity: ['#openai_verbosity', 'verbosity', false, false],
345353 enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false],
346354 seed: ['#seed_openai', 'seed', false, false],
347355 n: ['#n_openai', 'n', false, false],
@@ -442,6 +450,7 @@ const default_settings = {
442450 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
443451 show_thoughts: true,
444452 reasoning_effort: reasoning_effort_types.auto,
453+ verbosity: verbosity_levels.auto,
445454 enable_web_search: false,
446455 request_images: false,
447456 seed: -1,
@@ -2226,6 +2235,15 @@ function getReasoningEffort() {
22262235 return reasoningEffort;
22272236}
22282237
2238+function getVerbosity() {
2239+ if (oai_settings.verbosity === verbosity_levels.auto) {
2240+ return undefined;
2241+ }
2242+
2243+ // TODO: Adjust verbosity based on model capabilities
2244+ return oai_settings.verbosity;
2245+}
2246+
22292247/**
22302248 * Send a chat completion request to backend
22312249 * @param {string} type (impersonate, quiet, continue, etc)
@@ -2313,6 +2331,7 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
23132331 'enable_web_search': Boolean(oai_settings.enable_web_search),
23142332 'request_images': Boolean(oai_settings.request_images),
23152333 'custom_prompt_post_processing': oai_settings.custom_prompt_post_processing,
2334+ 'verbosity': getVerbosity(),
23162335 };
23172336
23182337 if (isAzureOpenAI) {
@@ -6296,6 +6315,11 @@ export function initOpenAI() {
62966315 saveSettingsDebounced();
62976316 });
62986317
6318+ $('#openai_verbosity').on('input', function () {
6319+ oai_settings.verbosity = String($(this).val());
6320+ saveSettingsDebounced();
6321+ });
6322+
62996323 $('#openai_enable_web_search').on('input', function () {
63006324 oai_settings.enable_web_search = !!$(this).prop('checked');
63016325 calculateOpenRouterCost();
src/constants.js+2 -0
@@ -453,6 +453,8 @@ export const AZURE_OPENAI_KEYS = [
453453 'reasoning_effort',
454454];
455455
456+export const OPENAI_VERBOSITY_MODELS = /^gpt-5/;
457+
456458export const OPENAI_REASONING_EFFORT_MODELS = [
457459 'o1',
458460 'o3-mini',
src/endpoints/backends/chat-completions.js+19 -0
@@ -11,6 +11,7 @@ import {
1111 GEMINI_SAFETY,
1212 OPENAI_REASONING_EFFORT_MAP,
1313 OPENAI_REASONING_EFFORT_MODELS,
14+ OPENAI_VERBOSITY_MODELS,
1415 OPENROUTER_HEADERS,
1516 VERTEX_SAFETY,
1617 ZAI_ENDPOINT,
@@ -163,6 +164,7 @@ async function sendClaudeRequest(request, response) {
163164 const useThinking = /^claude-(3-7|opus-4|sonnet-4|haiku-4-5|opus-4-5)/.test(request.body.model);
164165 const useWebSearch = /^claude-(3-5|3-7|opus-4|sonnet-4|haiku-4-5|opus-4-5)/.test(request.body.model) && Boolean(request.body.enable_web_search);
165166 const isLimitedSampling = /^claude-(opus-4-1|sonnet-4-5|haiku-4-5|opus-4-5)/.test(request.body.model);
167+ const useVerbosity = /^claude-(opus-4-5)/.test(request.body.model);
166168 const cacheTTL = getConfigValue('claude.extendedTTL', false, 'boolean') ? '1h' : '5m';
167169 let fixThinkingPrefill = false;
168170 // Add custom stop sequences
@@ -268,6 +270,13 @@ async function sendClaudeRequest(request, response) {
268270 convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user';
269271 }
270272
273+ // Verbosity = 'effort' (same values as OpenAI)
274+ if (useVerbosity && request.body.verbosity) {
275+ betaHeaders.push('effort-2025-11-24');
276+ requestBody.output_config ??= {};
277+ requestBody.output_config.effort = request.body.verbosity;
278+ }
279+
271280 if (betaHeaders.length) {
272281 additionalHeaders['anthropic-beta'] = betaHeaders.join(',');
273282 }
@@ -1835,6 +1844,10 @@ router.post('/generate', function (request, response) {
18351844 bodyParams['reasoning'] = { effort: request.body.reasoning_effort };
18361845 }
18371846
1847+ if (request.body.verbosity) {
1848+ bodyParams['verbosity'] = request.body.verbosity;
1849+ }
1850+
18381851 if (request.body.json_schema) {
18391852 bodyParams['response_format'] = {
18401853 type: 'json_schema',
@@ -2017,6 +2030,12 @@ router.post('/generate', function (request, response) {
20172030 }
20182031 }
20192032
2033+ if (request.body.verbosity && [CHAT_COMPLETION_SOURCES.CUSTOM, CHAT_COMPLETION_SOURCES.OPENAI].includes(request.body.chat_completion_source)) {
2034+ if (OPENAI_VERBOSITY_MODELS.test(request.body.model)) {
2035+ bodyParams['verbosity'] = request.body.verbosity;
2036+ }
2037+ }
2038+
20202039 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
20212040 console.warn('OpenAI API key is missing.');
20222041 return response.status(400).send({ error: true });