-
Notifications
You must be signed in to change notification settings - Fork 878
Add a custom API request body setting #1100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,9 @@ | |
| "Override provider temperature": "Override provider temperature", | ||
| "The temperature parameter is not sent. The provider or model default is used.": "The temperature parameter is not sent. The provider or model default is used.", | ||
| "The current model does not accept a custom temperature. The parameter will not be sent.": "The current model does not accept a custom temperature. The parameter will not be sent.", | ||
| "Extra Request Body (JSON)": "Extra Request Body (JSON)", | ||
| "Merged into the API request body. Must be a JSON object, other values are ignored.": "Merged into the API request body. Must be a JSON object, other values are ignored.", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3. New locale entries exceed 100 columns The added English guidance entry on line 128 repeats a long key and value on one physical line, exceeding 100 characters. The corresponding new entries in both Chinese locale files also remain on long physical lines. Agent Prompt
|
||
| "Invalid JSON object, this value is ignored.": "Invalid JSON object, this value is ignored.", | ||
|
Comment on lines
+127
to
+129
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2. Most locales lack the new setting text The three new English localization keys are added only to English, Simplified Chinese, and Traditional Chinese resources. The setting renders through t(), but the ten other supported locale files omit its label and guidance keys and fall back to English. Agent Prompt
|
||
| "API Url": "API Url", | ||
| "Provider": "Provider", | ||
| "Others": "Others", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -118,6 +118,9 @@ | |||||
| "Override provider temperature": "覆寫供應商的溫度參數", | ||||||
| "The temperature parameter is not sent. The provider or model default is used.": "不會傳送溫度參數,將使用供應商或模型的預設值。", | ||||||
| "The current model does not accept a custom temperature. The parameter will not be sent.": "目前的模型不接受自訂溫度參數,因此不會傳送這個參數。", | ||||||
| "Extra Request Body (JSON)": "額外請求參數 (JSON)", | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: This label describes the field as extra request parameters instead of an extra request body, which can make users look for query or other parameter settings rather than JSON merged into the body. Translate it consistently with the helper text, for example Prompt for AI agents
Suggested change
|
||||||
| "Merged into the API request body. Must be a JSON object, other values are ignored.": "會合併進 API 請求主體,必須是 JSON 物件,其他類型的值會被忽略。", | ||||||
| "Invalid JSON object, this value is ignored.": "不是合法的 JSON 物件,這個值會被忽略。", | ||||||
| "API Url": "API 網址", | ||||||
| "Provider": "供應商", | ||||||
| "Others": "其他", | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -855,6 +855,7 @@ export const defaultConfig = { | |
| maxConversationContextLength: 9, | ||
| temperatureOverrideEnabled: false, | ||
| temperature: 1, | ||
| extraBody: '', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 8. Provider settings reach other providers extraBody is one persisted setting that all three request builders merge without regard to the selected provider. After a user sets the documented Claude thinking example and switches to OpenAI, the OpenAI request still contains that Claude-specific object; switching back similarly carries OpenAI-specific fields into Claude requests. Agent Prompt
|
||
| customChatGptWebApiUrl: 'https://chatgpt.com', | ||
| customChatGptWebApiPath: '/backend-api/conversation', | ||
| customOpenAiApiUrl: 'https://api.openai.com', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import { isEmpty } from 'lodash-es' | |
| import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs' | ||
| import { getModelValue } from '../../utils/model-name-convert.mjs' | ||
| import { getTemperatureParams } from './temperature-params.mjs' | ||
| import { getExtraBodyParams } from './extra-body-params.mjs' | ||
|
|
||
| function getThinkingConfig(model) { | ||
| if (model === 'claude-sonnet-5') return { type: 'disabled' } | ||
|
|
@@ -37,6 +38,8 @@ export async function generateAnswersWithClaudeApi(port, question, session) { | |
| } | ||
| const thinking = getThinkingConfig(model) | ||
| if (thinking) body.thinking = thinking | ||
| // The user-provided body wins over the built-in defaults above. | ||
| Object.assign(body, getExtraBodyParams(config)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 7. Claude tool calls end in an error generateAnswersWithClaudeApi accepts configured tools and tool_choice fields, but its stream handler consumes only text deltas and treats a tool_use stop reason as incomplete. If Claude responds with a tool call, the handler neither executes it nor sends a tool result, and instead throws a completion error. Agent Prompt
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Keep Prompt for AI agentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Prompt for AI agents |
||
|
|
||
| let answer = '' | ||
| let stopReason = '' | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| * Parse the user-provided extra request body. | ||
| * @param {unknown} raw JSON text from the advanced settings textarea | ||
| * @returns {Record<string, unknown> | null} the parsed object, or null when unusable | ||
| */ | ||
| export function parseExtraBody(raw) { | ||
| if (typeof raw !== 'string' || !raw.trim()) return null | ||
| let parsed | ||
| try { | ||
| parsed = JSON.parse(raw) | ||
| } catch { | ||
| return null | ||
| } | ||
| if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null | ||
| return parsed | ||
| } | ||
|
|
||
| /** | ||
| * Keys every request builder owns. A custom body may add parameters the UI does | ||
| * not expose, but replacing these would desync the request from the conversation, | ||
| * model and settings the user picked, and `stream` must stay on for SSE parsing. | ||
| */ | ||
| const RESERVED_KEYS = ['stream', 'model', 'messages', 'prompt', 'temperature'] | ||
|
|
||
| /** | ||
| * Extra fields merged into the API request body. | ||
| * @param {UserConfig} config | ||
| * @returns {Record<string, unknown>} | ||
| */ | ||
| export function getExtraBodyParams(config) { | ||
| const extraBody = parseExtraBody(config?.extraBody) | ||
| if (!extraBody) return {} | ||
| for (const key of RESERVED_KEYS) delete extraBody[key] | ||
| return extraBody | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import assert from 'node:assert/strict' | ||
| import { test } from 'node:test' | ||
| import { | ||
| getExtraBodyParams, | ||
| parseExtraBody, | ||
| } from '../../../src/services/apis/extra-body-params.mjs' | ||
|
|
||
| test('parseExtraBody accepts a JSON object', () => { | ||
| assert.deepEqual(parseExtraBody('{"thinking":{"type":"enabled","budget_tokens":2048}}'), { | ||
| thinking: { type: 'enabled', budget_tokens: 2048 }, | ||
| }) | ||
| }) | ||
|
|
||
| test('parseExtraBody rejects anything that is not a JSON object', () => { | ||
| for (const raw of ['', ' ', 'not json', '[1,2]', '"text"', '42', 'null', null, undefined, {}]) { | ||
| assert.equal(parseExtraBody(raw), null, `expected null for ${JSON.stringify(raw)}`) | ||
| } | ||
| }) | ||
|
|
||
| test('getExtraBodyParams is empty without a usable config value', () => { | ||
| assert.deepEqual(getExtraBodyParams(undefined), {}) | ||
| assert.deepEqual(getExtraBodyParams({}), {}) | ||
| assert.deepEqual(getExtraBodyParams({ extraBody: '{oops' }), {}) | ||
| }) | ||
|
|
||
| test('getExtraBodyParams forwards user fields and keeps stream under extension control', () => { | ||
| const config = { extraBody: '{"reasoning_effort":"high","stream":false}' } | ||
|
|
||
| assert.deepEqual(getExtraBodyParams(config), { reasoning_effort: 'high' }) | ||
| // The parsed object is rebuilt per call, so repeated reads stay stripped. | ||
| assert.deepEqual(getExtraBodyParams(config), { reasoning_effort: 'high' }) | ||
| }) | ||
|
|
||
| test('getExtraBodyParams strips every key the request builders own', () => { | ||
| const config = { | ||
| extraBody: JSON.stringify({ | ||
| reasoning_effort: 'high', | ||
| top_p: 0.9, | ||
| stream: false, | ||
| model: 'gpt-4o', | ||
| messages: [{ role: 'user', content: 'tampered' }], | ||
| prompt: 'tampered', | ||
| temperature: 0.1, | ||
| }), | ||
| } | ||
|
|
||
| assert.deepEqual(getExtraBodyParams(config), { reasoning_effort: 'high', top_p: 0.9 }) | ||
| }) |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: Add the new setting strings to every supported locale; users of the other ten locales currently fall back to English for this UI.
Prompt for AI agents