fix: separate Anthropic output and context limits
Direct Anthropic connections reused the configured context window as max_tokens, which could impose an unintended output value or send a provider-invalid request. Anthropic connections now expose an independent per-response maximum output setting, defaulting to 32K for the protocol-required field, while OpenAI direct requests remain without an added output token cap and direct tool rounds remain unmetered. Release note: Anthropic 直连模型现在可单独配置单次最大输出,不再复用上下文窗口;OpenAI 直连请求和工具调用轮次不增加额外配额。
This commit is contained in:
@@ -1112,6 +1112,10 @@ describe('SettingsPanel runtime files', () => {
|
||||
expect(contextWindow).toHaveValue(null)
|
||||
expect(contextWindow).toHaveAttribute('min', '32')
|
||||
fireEvent.change(contextWindow, { target: { value: '256' } })
|
||||
const maximumOutput = screen.getByLabelText('Anthropic 最大输出')
|
||||
expect(maximumOutput).toHaveValue(32)
|
||||
expect(maximumOutput).toHaveAttribute('min', '1')
|
||||
fireEvent.change(maximumOutput, { target: { value: '48' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
||||
|
||||
await waitFor(() =>
|
||||
@@ -1120,7 +1124,8 @@ describe('SettingsPanel runtime files', () => {
|
||||
modelProfiles: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: modelProfileId,
|
||||
contextWindowTokens: 256_000
|
||||
contextWindowTokens: 256_000,
|
||||
maximumOutputTokens: 48_000
|
||||
})
|
||||
])
|
||||
})
|
||||
|
||||
@@ -34,6 +34,7 @@ import type {
|
||||
} from '../../shared/contracts'
|
||||
import {
|
||||
defaultContextCompressionSettings,
|
||||
defaultAnthropicMaximumOutputTokens,
|
||||
builtinEmbeddingConnectionId,
|
||||
defaultModelProfileId as builtInDefaultModelProfileId,
|
||||
defaultRuntimeSettings,
|
||||
@@ -257,6 +258,9 @@ function toModelProfileDrafts(
|
||||
return settings.modelProfiles.map((profile) => ({
|
||||
...profile,
|
||||
supportsImageInput: profile.supportsImageInput ?? false,
|
||||
maximumOutputTokens:
|
||||
profile.maximumOutputTokens ??
|
||||
defaultAnthropicMaximumOutputTokens,
|
||||
apiKey: '',
|
||||
clearApiKey: false
|
||||
}))
|
||||
@@ -1285,6 +1289,7 @@ export function SettingsPanel({
|
||||
authentication: profile.authentication,
|
||||
supportsImageInput: profile.supportsImageInput,
|
||||
contextWindowTokens: profile.contextWindowTokens,
|
||||
maximumOutputTokens: profile.maximumOutputTokens,
|
||||
imageGenerationQuality: profile.imageGenerationQuality,
|
||||
apiKey: profile.clearApiKey
|
||||
? ({ action: 'clear' } as const)
|
||||
@@ -1670,6 +1675,7 @@ export function SettingsPanel({
|
||||
protocol: 'openai-chat-completions',
|
||||
authentication: defaultRuntimeSettings.modelAuthentication,
|
||||
supportsImageInput: defaultRuntimeSettings.supportsImageInput,
|
||||
maximumOutputTokens: defaultAnthropicMaximumOutputTokens,
|
||||
imageGenerationQuality:
|
||||
defaultRuntimeSettings.imageGenerationQuality,
|
||||
apiKeyConfigured: false,
|
||||
@@ -3136,6 +3142,34 @@ export function SettingsPanel({
|
||||
{t('model.profile.contextWindowDescription')}
|
||||
</small>
|
||||
</label>
|
||||
{profile.protocol === 'anthropic-messages' && (
|
||||
<label className="field">
|
||||
<span>{t('model.profile.maximumOutput')}</span>
|
||||
<input
|
||||
aria-label={t('model.profile.maximumOutput')}
|
||||
inputMode="numeric"
|
||||
max={10_000}
|
||||
min={1}
|
||||
onChange={(event) => {
|
||||
const value = event.target.valueAsNumber
|
||||
updateModelProfile(profile.id, {
|
||||
maximumOutputTokens: Number.isFinite(value)
|
||||
? Math.round(value * 1_000)
|
||||
: defaultAnthropicMaximumOutputTokens
|
||||
})
|
||||
}}
|
||||
placeholder="32"
|
||||
type="number"
|
||||
value={
|
||||
(profile.maximumOutputTokens ??
|
||||
defaultAnthropicMaximumOutputTokens) / 1_000
|
||||
}
|
||||
/>
|
||||
<small>
|
||||
{t('model.profile.maximumOutputDescription')}
|
||||
</small>
|
||||
</label>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{profile.protocol ===
|
||||
|
||||
@@ -721,6 +721,9 @@ export const settings = {
|
||||
contextWindow: 'Context window (optional)',
|
||||
contextWindowDescription:
|
||||
'Enter 32K–10000K tokens. Leave blank when unknown. This value is used only for GoodBuddy local budget calculations.',
|
||||
maximumOutput: 'Anthropic maximum output',
|
||||
maximumOutputDescription:
|
||||
'The Anthropic Messages protocol requires this value on every request. Enter 1K–10000K tokens. It limits only one model response, not tool rounds or cumulative usage.',
|
||||
imageQuality: 'Image quality',
|
||||
imageQualityAriaLabel: 'Image quality for {{name}}',
|
||||
quality: {
|
||||
|
||||
@@ -662,6 +662,9 @@ export const settings = {
|
||||
contextWindow: '上下文上限(可选)',
|
||||
contextWindowDescription:
|
||||
'以 K tokens 填写,范围为 32K–10000K。留空表示未知;此值仅用于 GoodBuddy 本地预算计算。',
|
||||
maximumOutput: 'Anthropic 最大输出',
|
||||
maximumOutputDescription:
|
||||
'Anthropic Messages 协议要求每次请求提供该值。填写 1K–10000K tokens;它只限制单次模型响应,不限制工具调用轮次或累计用量。',
|
||||
imageQuality: '图片质量',
|
||||
imageQualityAriaLabel: '图片质量 {{name}}',
|
||||
quality: {
|
||||
|
||||
Reference in New Issue
Block a user