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

965b86da628297e7c39cd434ce901b4c2bfbf8ec

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

Signed
4 files changed, +61 -0Ignore whitespace
public/index.html+16 -0
@@ -2078,6 +2078,22 @@
2078 </div>2078 </div>
2079 </div>2079 </div>
2080 </div>2080 </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>
2081 <div class="range-block" data-source="claude">2097 <div class="range-block" data-source="claude">
2082 <div class="wide100p">2098 <div class="wide100p">
2083 <div class="flex-container alignItemsCenter">2099 <div class="flex-container alignItemsCenter">
public/scripts/openai.js+24 -0
@@ -239,6 +239,13 @@ export const reasoning_effort_types = {
239 max: 'max',239 max: 'max',
240};240};
241241
242export const verbosity_levels = {
243 auto: 'auto',
244 low: 'low',
245 medium: 'medium',
246 high: 'high',
247};
248
242export const ZAI_ENDPOINT = {249export const ZAI_ENDPOINT = {
243 COMMON: 'common',250 COMMON: 'common',
244 CODING: 'coding',251 CODING: 'coding',
@@ -342,6 +349,7 @@ export const settingsToUpdate = {
342 function_calling: ['#openai_function_calling', 'function_calling', true, false],349 function_calling: ['#openai_function_calling', 'function_calling', true, false],
343 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false],350 show_thoughts: ['#openai_show_thoughts', 'show_thoughts', true, false],
344 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false],351 reasoning_effort: ['#openai_reasoning_effort', 'reasoning_effort', false, false],
352 verbosity: ['#openai_verbosity', 'verbosity', false, false],
345 enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false],353 enable_web_search: ['#openai_enable_web_search', 'enable_web_search', true, false],
346 seed: ['#seed_openai', 'seed', false, false],354 seed: ['#seed_openai', 'seed', false, false],
347 n: ['#n_openai', 'n', false, false],355 n: ['#n_openai', 'n', false, false],
@@ -442,6 +450,7 @@ const default_settings = {
442 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,450 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
443 show_thoughts: true,451 show_thoughts: true,
444 reasoning_effort: reasoning_effort_types.auto,452 reasoning_effort: reasoning_effort_types.auto,
453 verbosity: verbosity_levels.auto,
445 enable_web_search: false,454 enable_web_search: false,
446 request_images: false,455 request_images: false,
447 seed: -1,456 seed: -1,
@@ -2226,6 +2235,15 @@ function getReasoningEffort() {
2226 return reasoningEffort;2235 return reasoningEffort;
2227}2236}
22282237
2238function 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
2229/**2247/**
2230 * Send a chat completion request to backend2248 * Send a chat completion request to backend
2231 * @param {string} type (impersonate, quiet, continue, etc)2249 * @param {string} type (impersonate, quiet, continue, etc)
@@ -2313,6 +2331,7 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
2313 'enable_web_search': Boolean(oai_settings.enable_web_search),2331 'enable_web_search': Boolean(oai_settings.enable_web_search),
2314 'request_images': Boolean(oai_settings.request_images),2332 'request_images': Boolean(oai_settings.request_images),
2315 'custom_prompt_post_processing': oai_settings.custom_prompt_post_processing,2333 'custom_prompt_post_processing': oai_settings.custom_prompt_post_processing,
2334 'verbosity': getVerbosity(),
2316 };2335 };
23172336
2318 if (isAzureOpenAI) {2337 if (isAzureOpenAI) {
@@ -6296,6 +6315,11 @@ export function initOpenAI() {
6296 saveSettingsDebounced();6315 saveSettingsDebounced();
6297 });6316 });
62986317
6318 $('#openai_verbosity').on('input', function () {
6319 oai_settings.verbosity = String($(this).val());
6320 saveSettingsDebounced();
6321 });
6322
6299 $('#openai_enable_web_search').on('input', function () {6323 $('#openai_enable_web_search').on('input', function () {
6300 oai_settings.enable_web_search = !!$(this).prop('checked');6324 oai_settings.enable_web_search = !!$(this).prop('checked');
6301 calculateOpenRouterCost();6325 calculateOpenRouterCost();
src/constants.js+2 -0
@@ -453,6 +453,8 @@ export const AZURE_OPENAI_KEYS = [
453 'reasoning_effort',453 'reasoning_effort',
454];454];
455455
456export const OPENAI_VERBOSITY_MODELS = /^gpt-5/;
457
456export const OPENAI_REASONING_EFFORT_MODELS = [458export const OPENAI_REASONING_EFFORT_MODELS = [
457 'o1',459 'o1',
458 'o3-mini',460 'o3-mini',
src/endpoints/backends/chat-completions.js+19 -0
@@ -11,6 +11,7 @@ import {
11 GEMINI_SAFETY,11 GEMINI_SAFETY,
12 OPENAI_REASONING_EFFORT_MAP,12 OPENAI_REASONING_EFFORT_MAP,
13 OPENAI_REASONING_EFFORT_MODELS,13 OPENAI_REASONING_EFFORT_MODELS,
14 OPENAI_VERBOSITY_MODELS,
14 OPENROUTER_HEADERS,15 OPENROUTER_HEADERS,
15 VERTEX_SAFETY,16 VERTEX_SAFETY,
16 ZAI_ENDPOINT,17 ZAI_ENDPOINT,
@@ -163,6 +164,7 @@ async function sendClaudeRequest(request, response) {
163 const useThinking = /^claude-(3-7|opus-4|sonnet-4|haiku-4-5|opus-4-5)/.test(request.body.model);164 const useThinking = /^claude-(3-7|opus-4|sonnet-4|haiku-4-5|opus-4-5)/.test(request.body.model);
164 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);165 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);
165 const isLimitedSampling = /^claude-(opus-4-1|sonnet-4-5|haiku-4-5|opus-4-5)/.test(request.body.model);166 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);
166 const cacheTTL = getConfigValue('claude.extendedTTL', false, 'boolean') ? '1h' : '5m';168 const cacheTTL = getConfigValue('claude.extendedTTL', false, 'boolean') ? '1h' : '5m';
167 let fixThinkingPrefill = false;169 let fixThinkingPrefill = false;
168 // Add custom stop sequences170 // Add custom stop sequences
@@ -268,6 +270,13 @@ async function sendClaudeRequest(request, response) {
268 convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user';270 convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user';
269 }271 }
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
271 if (betaHeaders.length) {280 if (betaHeaders.length) {
272 additionalHeaders['anthropic-beta'] = betaHeaders.join(',');281 additionalHeaders['anthropic-beta'] = betaHeaders.join(',');
273 }282 }
@@ -1835,6 +1844,10 @@ router.post('/generate', function (request, response) {
1835 bodyParams['reasoning'] = { effort: request.body.reasoning_effort };1844 bodyParams['reasoning'] = { effort: request.body.reasoning_effort };
1836 }1845 }
18371846
1847 if (request.body.verbosity) {
1848 bodyParams['verbosity'] = request.body.verbosity;
1849 }
1850
1838 if (request.body.json_schema) {1851 if (request.body.json_schema) {
1839 bodyParams['response_format'] = {1852 bodyParams['response_format'] = {
1840 type: 'json_schema',1853 type: 'json_schema',
@@ -2017,6 +2030,12 @@ router.post('/generate', function (request, response) {
2017 }2030 }
2018 }2031 }
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
2020 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {2039 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
2021 console.warn('OpenAI API key is missing.');2040 console.warn('OpenAI API key is missing.');
2022 return response.status(400).send({ error: true });2041 return response.status(400).send({ error: true });